home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Dev / SmallTalk / mstcomp.c < prev    next >
C/C++ Source or Header  |  1995-08-25  |  83KB  |  3,079 lines

  1. /***********************************************************************
  2.  *
  3.  *    Byte code compiler.
  4.  *
  5.  ***********************************************************************/
  6.  
  7. /***********************************************************************
  8.  *
  9.  * Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
  10.  * Written by Steve Byrne.
  11.  *
  12.  * This file is part of GNU Smalltalk.
  13.  *
  14.  * GNU Smalltalk is free software; you can redistribute it and/or modify it
  15.  * under the terms of the GNU General Public License as published by the Free
  16.  * Software Foundation; either version 1, or (at your option) any later 
  17.  * version.
  18.  * 
  19.  * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
  20.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  21.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  22.  * more details.
  23.  * 
  24.  * You should have received a copy of the GNU General Public License along with
  25.  * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
  26.  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
  27.  *
  28.  ***********************************************************************/
  29.  
  30.  
  31. /*
  32.  *    Change Log
  33.  * ============================================================================
  34.  * Author      Date       Change 
  35.  * sbb         19 Apr 91      Added skipCompilation boolean, for conditional
  36.  *              compilation.
  37.  *
  38.  * sbb         16 Feb 91      Recursive calls to equalConstant had the arguments
  39.  *              reversed.
  40.  *
  41.  * sbb         26 Nov 90      Fixed whileTrue: and whileFalse: to loop only if the
  42.  *              value returned by the receiver is the expected one,
  43.  *              instead of if it's the boolean not of the expected
  44.  *              value.
  45.  *
  46.  * sbb         10 Nov 90      Added support for retaining the latest compiled
  47.  *              method so the interpreter can return it from the
  48.  *              compile: primitive.
  49.  *
  50.  * sbb         21 Sep 90      Fixed so that a block that contains no statements
  51.  *              properly returns nil.
  52.  *
  53.  * sbyrne    20 May 90      Improved error handling...compiler errors set a flag,
  54.  *              and execution does not occur if the expression to be
  55.  *              executed has compilation errors.
  56.  *
  57.  * sbyrne    16 May 90      Added usage of emacsProcess.
  58.  *
  59.  * sbyrne    20 Apr 90      Fixed compiler to reset the byte code system before
  60.  *              using it.  The problem was if an error occurred, the
  61.  *              old byte code stream was still in use, and further
  62.  *              compilations were losing in a big way.
  63.  *
  64.  * sbyrne    25 Mar 90      Changed cache hit ratio reporting to check for divide
  65.  *              by zero, and to cast the byte counter to double (it
  66.  *              was casting to float and relying on promotion).
  67.  *
  68.  * sbyrne    13 Jan 90      Added support for "thisContext" as a compiler
  69.  *              built-in variable.
  70.  *
  71.  * sbyrne    28 Dec 89      Compiled methods now record their exact number of
  72.  *              byte codes.  Previously, if the byte codes didn't
  73.  *              exactly fill to a word-boundary, there was no way to
  74.  *              distinguish that case.  Now, with the advent of
  75.  *              dumping byte codes from within Smalltalk, this has
  76.  *              become a necessity.
  77.  *
  78.  * sbyrne    27 Dec 89      Realloc literal vec wasn't reallocing in units of
  79.  *              sizeof(OOP), so after a while, the literal vector
  80.  *              wasn't big enough.  Typically most methods don't have
  81.  *              a lot of literals, so this was not a problem.
  82.  *
  83.  * sbyrne     2 Oct 89      Fixed a bug with compilation of cascaded messages.
  84.  *              see HACK ALERT below.
  85.  *
  86.  * sbyrne    21 Sep 89      Made compilation of methods from strings record the
  87.  *              source string.
  88.  *
  89.  * sbyrne    13 Sep 89      Various changes for garbage collector.
  90.  *
  91.  * sbyrne     2 Sep 89      Began adding support for the method descriptor
  92.  *              instance variable.
  93.  *
  94.  * sbyrne     2 Jan 89      I guess it should be stated somewhere: you'll notice
  95.  *              in the code that there are several places where I
  96.  *              could have taken a more "functional" (i.e. LISP
  97.  *              oriented call a function within a function call)
  98.  *              approach.  I chose not to because it can make
  99.  *              debugging easier, it doesn't slow down the code much,
  100.  *              and may help the reader to understand better what's
  101.  *              going on in the code.
  102.  *
  103.  * sbyrne     1 Jan 89      Created.
  104.  *
  105.  */
  106.  
  107.  
  108. #include "mst.h"
  109. #include "mstsym.h"
  110. #include "mstcomp.h"
  111. #include "msttree.h"
  112. #include "mstbyte.h"
  113. #include "mstdict.h"
  114. #include "mstoop.h"
  115. #include "mstinterp.h"
  116. #include "mstlex.h"
  117. #include <setjmp.h>
  118. #ifdef HAS_ALLOCA_H
  119. #include <alloca.h>
  120. #endif
  121. #ifdef old_code /* Sun Jan 27 00:35:00 1991 */
  122. /**/#include <sys/time.h>
  123. /**/#if defined(USG)
  124. /**/#include <sys/times.h>
  125. /**/#endif
  126. #endif /* old_code Sun Jan 27 00:35:00 1991 */
  127. #ifdef sun
  128. #include <sys/time.h>
  129. #include <sys/resource.h>
  130. #endif
  131.  
  132. #define LITERAL_VEC_CHUNK_SIZE        32
  133.  
  134. extern    long        cacheHits, cacheMisses;
  135.  
  136. typedef enum {
  137.   falseJump,
  138.   trueJump,
  139.   unconditionalJump
  140. } JumpType;
  141.  
  142. typedef enum {
  143.   methodContext,
  144.   blockContext
  145. } ContextType;
  146.  
  147. typedef struct CompiledMethodStruct *CompiledMethod;
  148.  
  149. typedef struct MethodInfoStruct {
  150.   OBJ_HEADER;
  151.   OOP        sourceCode;
  152.   OOP        category;
  153. } *MethodInfo;
  154.  
  155. typedef struct FileSegmentStruct {
  156.   OBJ_HEADER;
  157.   OOP        fileName;
  158.   OOP        startPos;
  159.   OOP        length;
  160. } *FileSegment;
  161.  
  162. /* These hold the compiler's notions of the current class for compilations,
  163.  * and the current category that compiled methods are to be placed into */
  164. OOP            thisClass, thisCategory;
  165.  
  166. /* This holds the CompiledMethod oop for the most recently compiled method.
  167.  * It is only really valid after a compile: has been done, but this is 
  168.  * the only place that its used. */
  169. OOP            latestCompiledMethod;
  170.  
  171. /* These flags control whether byte codes are printed after compilation,
  172.  * and whether regression testing is in effect (which causes any messages
  173.  * that the system prints out to become constant messages, i.e. no timing
  174.  * information is printed) */
  175. Boolean            declareTracing, regressionTesting;
  176.  
  177. /* If true, the normal execution information is supressed, and the prompt
  178.  * is emitted with a special marker character ahead of it to let the process
  179.  * filter know that the execution has completed. */
  180. Boolean                   emacsProcess = false;
  181.  
  182. /* If true, the compilation of a set of methods will be skipped completely;
  183.  * only syntax will be checked.  Set by primitive, cleared by grammar.
  184.  */
  185. Boolean            skipCompilation = false;
  186.  
  187. static Boolean        hasExtendedSuper, isSuper(), equalConstant(),
  188.             compileWhileLoop(), compileIfStatement(),
  189.             compileIfTrueFalseStatement(), compileAndOrStatement();
  190. static OOP        computeSelector(), makeConstantOOP(),
  191.             getMethodLiterals(), makeNewMethod(), methodNew(),
  192.             methodInfoNew(), fileSegmentNew();
  193. static ByteCodes    optimizeByteCodes(), compileSubExpression(),
  194.             compileSubExpressionWithGoto(),
  195.             compileDefaultValue();
  196. static int        computeLocationIndex(), isSpecialVariable(),
  197.             addConstant(), addSelector(), whichBuiltinSelector(),
  198.             addForcedSelector(), listLength(), addLiteral();
  199. static CompiledMethod    simpleMethodNew();
  200. static void         compileStatement(), compileExpression(),
  201.             compileSimpleExpression(), compileVariable(),
  202.             compileConstant(), compileBlock(),
  203.             compileStatements(), compileUnaryExpr(),
  204.             compileBinaryExpr(), compileKeywordExpr(),
  205.             compileSend(), compileCascadedMessage(),
  206.             compileAssignments(), addMethodClassVariable(),
  207.             compileJump(), compileKeywordList(),
  208.             initLiteralVec(), reallocLiteralVec(),
  209.             compileBlockArguments(), installMethod();
  210.  
  211. /* Used to abort really losing compiles, jumps back to the top level of the
  212.  * compiler */
  213. static jmp_buf        badMethod;
  214.  
  215. /* The vector of literals that the compiler uses to accumulate literal
  216.  * constants into */
  217. static OOP        *literalVec;
  218.  
  219. /* These indicate the current number of literals in the method being compiled
  220.  * and the current maximum allocated size of the literal vector */
  221. static int        numLiterals, literalVecMax;
  222.  
  223. /* HACK ALERT!! HACK ALERT!!  This variable is used for cascading.  The
  224.  * tree structure is all wrong for the code in cascade processing to find
  225.  * the receiver of the initial message.  What this does is when it's true,
  226.  * compileUnaryExpr, compileBinaryExpr, and compileKeywordExpr record
  227.  * its value, and clear the global (to prevent propagation to compilation
  228.  * of subnodes).  After compiling their receiver, if the saved value of
  229.  * the flag is true, they emit a dupStackTop, and continue compilation.
  230.  * Since cascaded sends are relatively rare, I figured that this was a better
  231.  * alternative than passing useless parameters around all the time.
  232.  */
  233. static Boolean        dupMessageReceiver = false;
  234.  
  235. /*
  236.  *    void installInitialMethods()
  237.  *
  238.  * Description
  239.  *
  240.  *    This routine does a very interesting thing.  It installs the inital
  241.  *    method, which is the primitive for "methodsFor:".  It does this by
  242.  *    creating a string that contains the method definition and then passing
  243.  *    this to the parser as an expression to be parsed and compiled.  Once
  244.  *    this has been installed, we can go ahead and begin loading the rest of
  245.  *    the Smalltalk method definitions, but until the "methodsFor:" method is
  246.  *    defined, we cannot begin to deal with
  247.  *    "!Object methodsFor: 'primitives'!".
  248.  *
  249.  */
  250. void installInitialMethods()
  251. {
  252.   char        *methodsForString;
  253.  
  254.   initDefaultCompilationEnvironment();
  255.  
  256.   methodsForString = "\
  257. methodsFor: aCategoryString \
  258.     <primitive: 150> \
  259. ";
  260.   initLexer(true);        /* tell the lexer we're doing internal
  261.                    compiles */
  262.  
  263.   pushSmalltalkString(stringNew(methodsForString));
  264.   yyparse();
  265.   popStream(false);        /* can't close a string! */
  266. }
  267.  
  268. /*
  269.  *    void initDefaultCompilationEnvironment()
  270.  *
  271.  * Description
  272.  *
  273.  *    Does what it says.
  274.  *
  275.  */
  276. void initDefaultCompilationEnvironment()
  277. {
  278.   setCompilationClass(behaviorClass);
  279.   setCompilationCategory(nilOOP);
  280. }
  281.  
  282. /*
  283.  *    void invokeInitBlocks()
  284.  *
  285.  * Description
  286.  *
  287.  *    This function will send a message to Smalltalk (the system dictionary)
  288.  *    asking it to invoke a set of initialization blocks.  There are methods
  289.  *    in Smalltalk that allow for the recording of blocks to be invoked after
  290.  *    image load, and this function sets that process in motion.
  291.  *
  292.  */
  293. void invokeInitBlocks()
  294. {
  295.   /* +++ this will eventually be replaced with the user-level callin */
  296.   prepareExecutionEnvironment();
  297.   pushOOP(smalltalkDictionary);
  298.   sendMessage(internString("doInits"), 0, false);
  299.   interpret();
  300.   finishExecutionEnvironment();
  301. }
  302.  
  303. /*
  304.  *    void setCompilationClass(classOOP)
  305.  *
  306.  * Description
  307.  *
  308.  *    Sets the compiler's notion of the class to compile methods into.
  309.  *
  310.  * Inputs
  311.  *
  312.  *    classOOP: 
  313.  *        An OOP for a Class object to compile method definitions into.
  314.  *
  315.  */
  316. void setCompilationClass(classOOP)
  317. OOP    classOOP;
  318. {
  319.   maybeMoveOOP(classOOP);
  320.   thisClass = classOOP;
  321. }
  322.  
  323. /*
  324.  *    void setCompilationCategory(categoryOOP)
  325.  *
  326.  * Description
  327.  *
  328.  *    Sets the compiler's notion of the current method category
  329.  *
  330.  * Inputs
  331.  *
  332.  *    categoryOOP: 
  333.  *        An OOP that indicates the category to be used.  Typically a
  334.  *        string.
  335.  *
  336.  */
  337. void setCompilationCategory(categoryOOP)
  338. OOP    categoryOOP;
  339. {
  340.   maybeMoveOOP(categoryOOP);
  341.   thisCategory = categoryOOP;
  342. }
  343.  
  344. /*
  345.  *    void copyCompileContext()
  346.  *
  347.  * Description
  348.  *
  349.  *    Called only during a GC flip, this routine copies the current
  350.  *    compilation context variables to new space, since they're part of the
  351.  *    "root set".
  352.  *
  353.  */
  354. void copyCompileContext()
  355. {
  356.   if (!isNil(thisClass)) {
  357.     localMaybeMoveOOP(thisClass);
  358.   }
  359.  
  360.   if (!isNil(thisCategory)) {
  361.     localMaybeMoveOOP(thisCategory);
  362.   }
  363. }
  364.  
  365. /*
  366.  *    void executeStatements(temporaries, statements, quiet)
  367.  *
  368.  * Description
  369.  *
  370.  *    Called to compile and execute an "immediate expression"; i.e. a set of
  371.  *    Smalltalk statements that are not part of a method definition.
  372.  *
  373.  * Inputs
  374.  *
  375.  *    temporaries: 
  376.  *        Syntax tree node that represents the temporary variables
  377.  *        associated with the expression.
  378.  *    statements: 
  379.  *        The statements of the expression.  A syntax tree node.
  380.  *    quiet : Flag to indicate either messages are to be output indicating
  381.  *        the commencement of execution and some timing results at the
  382.  *        end of execution.
  383.  *
  384.  */
  385. void executeStatements(temporaries, statements, quiet)
  386. TreeNode temporaries, statements;
  387. Boolean    quiet;
  388. {
  389.   TreeNode    messagePattern;
  390.   unsigned long startTime, endTime, deltaTime;
  391. #ifdef old_code /* Sat Jan  5 12:18:29 1991 */
  392. /**/#if !defined(USG)
  393. /**/  struct timeval startTime, endTime, deltaTime;
  394. /**/#else
  395. /**/  time_t startTime, endTime, deltaTime;
  396. /**/  struct tms dummy;
  397. /**/#endif
  398. #endif /* old_code Sat Jan  5 12:18:29 1991 */
  399. #ifdef sun
  400.   struct rusage startRusage, endRusage;
  401. #endif
  402.   OOP        returnedValue;
  403.  
  404.   setCompilationClass(objectClass);
  405.  
  406.   messagePattern = makeUnaryExpr(nil, "executeStatements");
  407.   compileMethod(makeMethod(messagePattern, temporaries, 0, statements), false);
  408.   if (hadError) {        /* don't execute on error */
  409.     return;
  410.   }
  411.  
  412.   /* send a message to NIL, which will find this synthetic method definition
  413.      in Object and execute it */
  414. /* dprintf("==============> ExecuteStatements\n"); */
  415.   prepareExecutionEnvironment();
  416. /* printContext(); */
  417.   pushOOP(nilOOP);
  418.   if (!quiet) {
  419.     printf("\nExecution begins...\n");
  420.   }
  421.   sendMessage(internString("executeStatements"), 0, false);
  422.   byteCodeCounter = 0;
  423.   startTime = getMilliTime();
  424. #ifdef sun
  425.   getrusage(RUSAGE_SELF, &startRusage);
  426. #endif
  427.   interpret();
  428. #ifdef sun
  429.   getrusage(RUSAGE_SELF, &endRusage);
  430. #endif
  431.   endTime = getMilliTime();
  432. #ifdef old_code /* Sat Jan  5 12:19:10 1991 */
  433. /**/#if !defined(USG)
  434. /**/  gettimeofday(&startTime, nil);
  435. /**/  interpret();
  436. /**/  gettimeofday(&endTime, nil);
  437. /**/#else
  438. /**/  startTime = times(&dummy);
  439. /**/  interpret();
  440. /**/  endTime = times(&dummy);
  441. /**/#endif
  442. #endif /* old_code Sat Jan  5 12:19:10 1991 */
  443.   returnedValue = finishExecutionEnvironment();
  444.   if (!quiet) {
  445.     if (!regressionTesting) {
  446.       printf("%d byte codes executed\n", byteCodeCounter);
  447.       deltaTime = endTime - startTime;
  448.       if (deltaTime <= 0) {
  449.      deltaTime = 1;        /* it could be zero which would core dump */
  450.       }
  451.       printf("which took %d.%03d seconds, giving %f bytecodes/sec\n",
  452.          deltaTime/1000, deltaTime%1000, (float)byteCodeCounter/
  453.          (deltaTime / 1000.0));
  454. #ifdef sun
  455.       deltaTime = ((endRusage.ru_utime.tv_sec * 1000) +
  456.                   (endRusage.ru_utime.tv_usec / 1000)) -
  457.           ((startRusage.ru_utime.tv_sec * 1000) +
  458.           (startRusage.ru_utime.tv_usec / 1000));
  459.       printf("(%d.%03d seconds user time, giving %f bytecodes/sec)\n",
  460.          deltaTime/1000, deltaTime%1000, (float)byteCodeCounter/
  461.          (deltaTime / 1000.0));
  462.       deltaTime = ((endRusage.ru_stime.tv_sec * 1000) +
  463.                   (endRusage.ru_stime.tv_usec / 1000)) -
  464.           ((startRusage.ru_stime.tv_sec * 1000) +
  465.           (startRusage.ru_stime.tv_usec / 1000));
  466.       printf("(%d.%03d seconds system time)\n",
  467.          deltaTime/1000, deltaTime%1000);
  468.       printf("(%d swaps, %d minor page faults, %d major page faults)\n",
  469.              endRusage.ru_nswap - startRusage.ru_nswap,
  470.              endRusage.ru_minflt - startRusage.ru_minflt,
  471.              endRusage.ru_majflt - startRusage.ru_majflt);
  472.   printf("(%d voluntary context switches, %d involuntary context switches)\n",
  473.           endRusage.ru_nvcsw - startRusage.ru_nvcsw,
  474.           endRusage.ru_nivcsw - startRusage.ru_nivcsw);
  475. #endif
  476.  
  477. #ifdef old_code /* Sat Jan  5 12:20:57 1991 */
  478. /**/#if !defined(USG)
  479. /**/      deltaTime.tv_sec = endTime.tv_sec - startTime.tv_sec;
  480. /**/      deltaTime.tv_usec = endTime.tv_usec - startTime.tv_usec;
  481. /**/      if (deltaTime.tv_usec < 0) {
  482. /**/    deltaTime.tv_sec--;
  483. /**/    deltaTime.tv_usec += 1000000;
  484. /**/      }
  485. /**/      if (deltaTime.tv_sec == 0 && deltaTime.tv_usec == 0) {
  486. /**/    deltaTime.tv_usec = 1;    /* fake a non-zero amount of time */
  487. /**/      }
  488. /**/      printf("which took %d.%d seconds, giving %f bytecodes/sec\n",
  489. /**/         deltaTime.tv_sec, deltaTime.tv_usec, (double)byteCodeCounter/
  490. /**/         (deltaTime.tv_sec + deltaTime.tv_usec/1000000.0));
  491. /**/#else
  492. /**/      deltaTime = endTime - startTime;
  493. /**/      if (deltaTime <= 0){
  494. /**/     deltaTime = 1;        /* it could be zero which would core dump */
  495. /**/      }
  496. /**/      printf("which took %d.%d seconds, giving %f bytecodes/sec\n",
  497. /**/         deltaTime/gethz(), deltaTime%gethz(), (float)byteCodeCounter/
  498. /**/         (deltaTime / (double) gethz()));
  499. /**/#endif
  500. #endif /* old_code Sat Jan  5 12:20:57 1991 */
  501.       if (cacheHits + cacheMisses) {
  502.     printf("%d cache hits, %d misses %f hit ratio\n",
  503.            cacheHits, cacheMisses,
  504.            (float)cacheHits / (cacheHits + cacheMisses));
  505.       } else {
  506.     printf("%d cache hits, %d misses\n",
  507.            cacheHits, cacheMisses);
  508.       }
  509.       
  510. {
  511.   extern long numMethods, numBlocks;
  512.   extern long totalMethods, totalRealized;
  513.    printf("Methods %d blocks %d, percentage %.2f\n",
  514.       numMethods, numBlocks, numBlocks * 100.0 / numMethods);
  515.   printf("Methods that weren't real: %d, %.2f%% of total\n", totalMethods,
  516.      totalMethods * 100.0 / numMethods);
  517.   printf("Average realized value %.4f, total %d\n", (float)totalRealized / numBlocks,
  518.      totalRealized);
  519. }
  520.  
  521. /*    approx 25% of byte codes are sends
  522.       printf("sends / bytecodes = %.1f\n",
  523.          (cacheHits + cacheMisses) * 100.0 / byteCodeCounter );
  524. */
  525. #ifdef countingByteCodes 
  526.       printByteCodeCounts();
  527. #endif
  528. #ifdef collision_checking
  529.       { int i;
  530.     extern int collide[];
  531.     for (i = 0; i < 2048; i++) {
  532.       if (collide[i]) {
  533.         printf("collide[%d] = %d\n", i, collide[i]);
  534.       }
  535.     }
  536.       }
  537. #endif /* collision_checking */
  538.     }
  539.  
  540.     printf("returned value is ");
  541.     printObject(returnedValue);
  542.     printf("\n");
  543.   }
  544. }
  545.  
  546.  
  547. /*
  548.  *    void compileMethod(method)
  549.  *
  550.  * Description
  551.  *
  552.  *    Compile the code for a complete method definition.  Special cases for
  553.  *    methods that don't return a value explicitly by returning "self".
  554.  *    Actually creates the CompiledMethod object and installs it in the
  555.  *    current method dictionary with the selector derived from the method
  556.  *    expression.
  557.  *
  558.  * Inputs
  559.  *
  560.  *    method: A syntax tree node for a method definition.
  561.  *
  562.  */
  563. void compileMethod(method)
  564. TreeNode method;
  565. {
  566.   TreeNode    statement;
  567.   OOP        selector;
  568.   ByteCodes    byteCodes;
  569.  
  570.   dupMessageReceiver = false;
  571.   latestCompiledMethod = nilOOP;
  572.  
  573.   initCompiler();
  574.   declareArguments(method->vMethod.selectorExpr);
  575.   declareTemporaries(method->vMethod.temporaries);
  576.  
  577.   if (setjmp(badMethod) == 0) {
  578.     for (statement = method->vMethod.statements; statement;
  579.      statement = statement->vExpr.expression) {
  580.       compileStatement(statement->vExpr.receiver);
  581.       if (statement->vExpr.receiver->nodeType != returnExprType) {
  582.     if (statement->vExpr.expression == nil) {
  583.       /* compile a return of self */
  584.       compileByte(returnIndexed | receiverIndex);
  585.     } else {
  586.       /* ignore the result of the last statement if it's not used */
  587.       compileByte(popStackTop);
  588.     }
  589.       }
  590.     }
  591.  
  592.     if (method->vMethod.statements == nil) {
  593.       /* special case an empty statement body to return self */
  594.       /* ??? this could compile some kind of primitive failure message, 
  595.      I guess */
  596.       compileByte(returnIndexed | receiverIndex);
  597.     }
  598.  
  599.     if (hasExtendedSuper) {
  600.       addMethodClassVariable();
  601.     }
  602.  
  603.     selector = computeSelector(method->vMethod.selectorExpr);
  604.     byteCodes = getByteCodes();
  605.     byteCodes = optimizeByteCodes(byteCodes);
  606.  
  607.     installMethod(selector, method->vMethod.primitiveIndex,
  608.           getArgCount(), getTempCount(),
  609.           getMethodLiterals(), byteCodes);
  610.   } else {
  611.     hadError = true;
  612.   }
  613.  
  614.   undeclareTemporaries(method->vMethod.temporaries);
  615.   undeclareArguments(method->vMethod.selectorExpr);
  616.   freeTree(method);
  617. }
  618.  
  619. /*
  620.  *    static void compileStatement(stmt)
  621.  *
  622.  * Description
  623.  *
  624.  *    Compiles a statement expression, including return expressions.
  625.  *
  626.  * Inputs
  627.  *
  628.  *    stmt  : A stmt tree node.
  629.  *
  630.  */
  631. static void compileStatement(stmt)
  632. TreeNode stmt;
  633. {
  634.   int        index;
  635.  
  636.   switch (stmt->nodeType) {
  637.   case constExprType:
  638.   case blockNodeType:
  639.     index = -1;
  640.     break;
  641.  
  642.   default:
  643.     index = isSpecialVariable(stmt->vExpr.receiver);
  644.   }
  645.  
  646.   if (index < 0) {
  647.     if (stmt->nodeType == returnExprType) {
  648.       compileExpression(stmt->vExpr.receiver);
  649.       compileByte(returnMethodStackTop);
  650.     } else {
  651.       compileExpression(stmt);
  652.     }
  653.   } else {
  654.     if (stmt->nodeType == returnExprType) {
  655.       /* return one of {self, true, false, nil} */
  656.       compileByte(returnIndexed | index);
  657.     } else {
  658.       compileExpression(stmt);
  659.     }
  660.   }
  661. }
  662.  
  663. /*
  664.  *    static void compileExpression(expr)
  665.  *
  666.  * Description
  667.  *
  668.  *    Compile an arbitrary expression, including an assignment expression.
  669.  *
  670.  * Inputs
  671.  *
  672.  *    expr  : A syntax tree node for an expression, including assignments.
  673.  *
  674.  */
  675. static void compileExpression(expr)
  676. TreeNode expr;
  677. {
  678.   if (expr->nodeType == assignExprType) {
  679.     compileSimpleExpression(expr->vExpr.expression);
  680.     compileAssignments(expr->vExpr.receiver);
  681.   } else {
  682.     compileSimpleExpression(expr);
  683.   }
  684. }
  685.  
  686. /*
  687.  *    static void compileSimpleExpression(expr)
  688.  *
  689.  * Description
  690.  *
  691.  *    The basic expression compiler.  Can be called recursively.  Dispatches
  692.  *    based on the type of the expression to different routines that
  693.  *    specialize in compilations for that expression.
  694.  *
  695.  * Inputs
  696.  *
  697.  *    expr  : A syntax tree node for some kind of expression.
  698.  *
  699.  */
  700. static void compileSimpleExpression(expr)
  701. TreeNode expr;
  702. {
  703.   switch (expr->nodeType) {
  704.   case variableNodeType:
  705.     compileVariable(expr);
  706.     break;
  707.   case constExprType:
  708.     compileConstant(expr);
  709.     break;
  710.   case blockNodeType:
  711.     compileBlock(expr);
  712.     break;
  713.   case unaryExprType:
  714.     compileUnaryExpr(expr);
  715.     break;
  716.   case binaryExprType:
  717.     compileBinaryExpr(expr);
  718.     break;
  719.   case keywordExprType:
  720.     compileKeywordExpr(expr);
  721.     break;
  722.   case cascadedMessageNodeType:
  723.     compileCascadedMessage(expr);
  724.     break;
  725.   default:
  726.     compileExpression(expr);
  727.   }
  728. }
  729.  
  730. /*
  731.  *    static void compileVariable(varName)
  732.  *
  733.  * Description
  734.  *
  735.  *    Compile code to push the value of a variable onto the stack.  The
  736.  *    special variables, self, true, false, super, and thisContext, are
  737.  *    handled specially.  For other variables, different code is emitted
  738.  *    depending on where the variable lives, such as in a global variable or
  739.  *    in a method temporary.
  740.  *
  741.  * Inputs
  742.  *
  743.  *    varName: 
  744.  *        A syntax tree node that indicates a variable name.
  745.  *
  746.  */
  747. static void compileVariable(varName)
  748. TreeNode varName;
  749. {
  750.   SymbolEntry    variable;
  751.   int        index, location;
  752.  
  753.   index = isSpecialVariable(varName);
  754.   if (index >= 0) {
  755.     compileByte(pushSpecial | index);
  756.     return;
  757.   }
  758.  
  759.   if (internString(varName->vList.name) == thisContextSymbol) {
  760.     compileByte(pushActiveContext);
  761.     return;
  762.   }
  763.  
  764.   variable = findVariable(varName->vList.name);
  765.   if (variable == nil) {
  766.     errorf("Undefined variable %s referenced", varName->vList.name);
  767.     longjmp(badMethod, 1);
  768.   }
  769.   
  770.   if (variable->scope == temporaryScope || variable->scope == receiverScope) {
  771.     if (variable->varIndex <= 15) {
  772.       if (variable->scope == temporaryScope) {
  773.     compileByte(pushTemporaryVariable | variable->varIndex);
  774.       } else {
  775.     compileByte(pushReceiverVariable | variable->varIndex);
  776.       }
  777.     } else {
  778.       compileByte(pushIndexed);
  779.       location = (variable->scope == temporaryScope)
  780.     ? temporaryLocation : receiverLocation;
  781.       compileByte(location | variable->varIndex);
  782.     }
  783.   } else {
  784.     if (variable->varIndex <= 31) {
  785.       compileByte(pushLitVariable | variable->varIndex);
  786.     } else {
  787.       /* ??? check for variable index too large here? */
  788.       compileByte(pushIndexed);
  789.       compileByte(litVarLocation | variable->varIndex);
  790.     }
  791.   }
  792.   freeSymbolEntry(variable);
  793. }
  794.  
  795. /*
  796.  *    static void compileConstant(constExpr)
  797.  *
  798.  * Description
  799.  *
  800.  *    Compile an expression that pushes a constant expression onto the stack.
  801.  *    Special cases out the constants that the byte code interpreter knows
  802.  *    about, which are the integers in the range -1 to 2.  Tries to emit the
  803.  *    shortest possible byte sequence.
  804.  *
  805.  * Inputs
  806.  *
  807.  *    constExpr: 
  808.  *        A syntax tree node that represents a literal constant.
  809.  *
  810.  */
  811. static void compileConstant(constExpr)
  812. TreeNode constExpr;
  813. {
  814.   int        index;
  815.  
  816.   index = addConstant(constExpr);
  817.   if (index < 0) {
  818.     compileByte(pushSpecial | (index + 3 + 5));
  819.   } else if (index <= 31) {
  820.     compileByte(pushLitConstant | index);
  821.   } else {
  822.     compileByte(pushIndexed);
  823.     compileByte(litConstLocation | index);
  824.   }
  825. }
  826.  
  827. /*
  828.  *    static void compileBlock(blockExpr)
  829.  *
  830.  * Description
  831.  *
  832.  *    Compile the expressions for a block.  Also, emits code to create the
  833.  *    block, and then to skip around it.  The block will have its initial
  834.  *    byte pointer pointing to two bytes past the long jump instruction, so
  835.  *    that when the block is invoked it will start off at the first byte of
  836.  *    the block.
  837.  *    
  838.  *
  839.  * Inputs
  840.  *
  841.  *    blockExpr: 
  842.  *        A syntax tree node for a block expression.
  843.  *
  844.  */
  845. static void compileBlock(blockExpr)
  846. TreeNode blockExpr;
  847. {
  848.   ByteCodes    currentByteCodes, blockByteCodes;
  849.  
  850.   currentByteCodes = saveByteCodeArray(); /* ??? don't like this name */
  851.  
  852.   declareBlockArguments(blockExpr->vMethod.temporaries);
  853.   compileBlockArguments(blockExpr->vMethod.temporaries);
  854.   compileStatements(blockExpr->vMethod.statements, true);
  855.   undeclareBlockArguments(blockExpr->vMethod.temporaries);
  856.  
  857.   blockByteCodes = getByteCodes();
  858.   restoreByteCodeArray(currentByteCodes);
  859.  
  860.   /* emit standard byte sequence to invoke a block:
  861.    *   push current context
  862.    *   push number of block args
  863.    *   blockCopy: send
  864.    *   long jump around block bytecodes
  865.    *   <block byte codes>...
  866.    */
  867.   compileByte(pushActiveContext);
  868.   compilePushIntConstant(listLength(blockExpr->vMethod.temporaries));
  869.   compileByte(blockCopyColonSpecial);
  870.   compileByte(jumpLong | (byteCodeLength(blockByteCodes)/256)+4);
  871.   compileByte(byteCodeLength(blockByteCodes) & 255);
  872.   compileAndFreeByteCodes(blockByteCodes);
  873.  
  874. }
  875.  
  876. /*
  877.  *    static void compileBlockArguments(args)
  878.  *
  879.  * Description
  880.  *
  881.  *    On entry to a block, its arguments (if any) are on the stack.  Since
  882.  *    there is no way to refer to them via byte codes on the stack, the
  883.  *    correct procedure is to pop them into temporary locations in the method
  884.  *    itself.  Since we're popping from a stack, we need to pop the arguments
  885.  *    in reverse order from the order that they are declared.  Args is a list
  886.  *    of TreeNodes in declaration order, so we recurse to get the last one,
  887.  *    pop it, get the penultimate, pop it, etc.  This routine works even if
  888.  *    args is nil, in which case the block had no arguments.
  889.  *
  890.  * Inputs
  891.  *
  892.  *    args  : a TreeNode of type ListNode that is a list of argument names to
  893.  *        the block being compiled.
  894.  *
  895.  */
  896. static void compileBlockArguments(args)
  897. TreeNode args;
  898. {
  899.   SymbolEntry    variable;
  900.  
  901.   if (args == nil) {
  902.     return;
  903.   }
  904.  
  905.   compileBlockArguments(args->vList.next);
  906.   variable = findVariable(args->vList.name);
  907.   if (variable->varIndex <= 7) {
  908.     compileByte(popTemporaryVariable | variable->varIndex);
  909.   } else {
  910.     compileByte(popStoreIndexed);
  911.     compileByte(temporaryLocation | variable->varIndex);
  912.   }
  913.   freeSymbolEntry(variable);
  914. }
  915.  
  916. /*
  917.  *    static void compileStatements(statementList, isBlock)
  918.  *
  919.  * Description
  920.  *
  921.  *    Compiles all of the statements in statement list.  Makes the final
  922.  *    instruction of the block be a return top of stack, if the final
  923.  *    statement isn't a return (^).
  924.  *
  925.  * Inputs
  926.  *
  927.  *    statementList: 
  928.  *        A TreeNode of type ExprNode that is the list of statements in
  929.  *        the block.  If it is nil, the block's return value is nil.
  930.  *    isBlock:A boolean.  If true, these statements are from a block
  931.  *        context.  If false, they are just an ordinary statement list.
  932.  */
  933. static void compileStatements(statementList, isBlock)
  934. TreeNode statementList;
  935. Boolean    isBlock;
  936. {
  937.   TreeNode    stmt;
  938.  
  939.   if (statementList == nil) {
  940.     if (isBlock) {
  941.       compileByte(pushSpecial | nilIndex);
  942.       compileByte(returnBlockStackTop);
  943.     } else {
  944.       compileByte(pushSpecial | nilIndex);
  945.     }
  946.     return;
  947.   }
  948.   
  949.   for(stmt = statementList ; stmt; stmt = stmt->vExpr.expression) {
  950.     compileStatement(stmt->vExpr.receiver);
  951.     if (stmt->vExpr.expression == nil) {
  952.       if (stmt->vExpr.receiver->nodeType != returnExprType) {
  953.     /* if last statement isn't a return, then return the value on the
  954.        stack as the result.  For non-block contexts, returning the top
  955.        of the stack is the default, so it's ok.*/
  956.     if (isBlock) {
  957.       compileByte(returnBlockStackTop);
  958.     }
  959.       }
  960.     } else {
  961.       /* throw away the value on the top of the stack...we don't need it
  962.      for all but the last one. */
  963.       compileByte(popStackTop);
  964.     }
  965.   }
  966. }
  967.  
  968.  
  969. /*
  970.  *    static void compileUnaryExpr(expr)
  971.  *
  972.  * Description
  973.  *
  974.  *    Compile code to evaluate a unary expression.  Special cases sends to
  975.  *    "super" and duplicates the receiver's value if this is part of a
  976.  *    cascaded message send.
  977.  *
  978.  * Inputs
  979.  *
  980.  *    expr  : A syntax tree node for a unary expression.
  981.  *
  982.  */
  983. static void compileUnaryExpr(expr)
  984. TreeNode expr;
  985. {
  986.   OOP        selector;
  987.   int        selectorIndex;
  988.   Boolean    savedDupFlag;
  989.  
  990.   savedDupFlag = dupMessageReceiver;
  991.   dupMessageReceiver = false;
  992.  
  993.   selector = expr->vExpr.selector;
  994.   selectorIndex = addSelector(selector);
  995.  
  996.   if (expr->vExpr.receiver != nil) {
  997.     compileExpression(expr->vExpr.receiver);
  998.     if (savedDupFlag) {
  999.       compileByte(dupStackTop);
  1000.     }
  1001.     if (isSuper(expr->vExpr.receiver)) {
  1002.       if (selectorIndex < 0) {
  1003.     selectorIndex = addForcedSelector(selector);
  1004.       }
  1005.       hasExtendedSuper = true;
  1006.       if (selectorIndex <= 31) {
  1007.     compileByte(sendSuper1ExtByte);
  1008.     compileByte(selectorIndex);
  1009.       } else {
  1010.     compileByte(sendSuper2ExtByte);
  1011.     compileByte(0);
  1012.     compileByte(selectorIndex);
  1013.       }
  1014.       return;
  1015.     }
  1016.   }
  1017.   
  1018.   if (isNil(selector)) {
  1019.     errorf("Nil selector in unary expression");
  1020.     longjmp(badMethod, 1);
  1021.   }
  1022.  
  1023.   if (selectorIndex < 0) {
  1024.     compileByte(-selectorIndex);
  1025.   } else {
  1026.     compileSend(selectorIndex, 0);
  1027.   }
  1028. }
  1029.  
  1030. /*
  1031.  *    static void compileBinaryExpr(expr)
  1032.  *
  1033.  * Description
  1034.  *
  1035.  *    Compiles code for a binary message.  Special cases sends to super, as
  1036.  *    they get different byte codes.  Also, checks to see if it's the first
  1037.  *    part of a cascaded message send and if so emits code to duplicate the
  1038.  *    stack top after the evaluation of the receiver for use by the
  1039.  *    subsequent cascaded expressions.
  1040.  *
  1041.  * Inputs
  1042.  *
  1043.  *    expr  : A syntax tree node for a binary expression.
  1044.  *
  1045.  */
  1046. static void compileBinaryExpr(expr)
  1047. TreeNode expr;
  1048. {
  1049.   OOP        selector;
  1050.   int        selectorIndex;
  1051.   Boolean    savedDupFlag;
  1052.  
  1053.   savedDupFlag = dupMessageReceiver;
  1054.   dupMessageReceiver = false;
  1055.  
  1056.   selector = expr->vExpr.selector;
  1057.   selectorIndex = addSelector(selector);
  1058.  
  1059.   if (expr->vExpr.receiver) {
  1060.     compileExpression(expr->vExpr.receiver);
  1061.     if (savedDupFlag) {
  1062.       compileByte(dupStackTop);
  1063.     }
  1064.   }
  1065.   if (expr->vExpr.expression) {
  1066.     compileExpression(expr->vExpr.expression);
  1067.   }
  1068.  
  1069.   if (expr->vExpr.receiver) {
  1070.     if (isSuper(expr->vExpr.receiver)) {
  1071.       if (selectorIndex < 0) {
  1072.     selectorIndex = addForcedSelector(selector);
  1073.       }
  1074.       hasExtendedSuper = true;
  1075.       if (selectorIndex <= 31) {
  1076.     compileByte(sendSuper1ExtByte);
  1077.     compileByte((1 << 5) | selectorIndex);
  1078.       } else {
  1079.     compileByte(sendSuper2ExtByte);
  1080.     compileByte(1);
  1081.     compileByte(selectorIndex);
  1082.       }
  1083.       return;
  1084.     }
  1085.   }
  1086.  
  1087.   if (isNil(selector)) {
  1088.     errorf("nil selector in binary expression");
  1089.     longjmp(badMethod, 1);
  1090.   }
  1091.  
  1092.   if (selectorIndex < 0) {
  1093.     compileByte(-selectorIndex);
  1094.   } else {
  1095.     compileSend(selectorIndex, 1);
  1096.   }
  1097. }
  1098.  
  1099. /*
  1100.  *    static void compileKeywordExpr(expr)
  1101.  *
  1102.  * Description
  1103.  *
  1104.  *    Compile an keyword message send.  Special cases out while loops, the 4
  1105.  *    kinds of if tests, and the conditional "and" and conditional "or"
  1106.  *    messages.  If the expression isn't one of these, the expression is
  1107.  *    evaluated normally.  Has special hacks to support the duplication of
  1108.  *    the receiver's value in the case of the first part of a cascaded
  1109.  *    message send.
  1110.  *
  1111.  * Inputs
  1112.  *
  1113.  *    expr  : A syntax tree node that represents a keyword message send
  1114.  *        expression.
  1115.  *
  1116.  */
  1117. static void compileKeywordExpr(expr)
  1118. TreeNode expr;
  1119. {
  1120.   OOP        selector;
  1121.   int        selectorIndex, numArgs;
  1122.   Boolean    savedDupFlag;
  1123.  
  1124.   savedDupFlag = dupMessageReceiver;
  1125.   dupMessageReceiver = false;
  1126.  
  1127.   selector = computeSelector(expr);
  1128.  
  1129.   /* check for optimized cases of messages to booleans and handle them
  1130.      specially */
  1131.   if (selector == whileTrueColonSymbol || selector == whileFalseColonSymbol) {
  1132.     if (compileWhileLoop(selector, expr)) {
  1133.       return;
  1134.     }
  1135.   }
  1136.  
  1137.   if (expr->vExpr.receiver) {
  1138.     compileExpression(expr->vExpr.receiver);
  1139.     if (savedDupFlag) {
  1140.       compileByte(dupStackTop);
  1141.     }
  1142.   }
  1143.  
  1144.   if (selector == ifTrueColonSymbol || selector == ifFalseColonSymbol) {
  1145.     if (compileIfStatement(selector, expr->vExpr.expression)) {
  1146.       return;
  1147.     }
  1148.   } else if (selector == ifTrueColonIfFalseColonSymbol
  1149.          || selector == ifFalseColonIfTrueColonSymbol) {
  1150.     if (compileIfTrueFalseStatement(selector, expr->vExpr.expression)) {
  1151.       return;
  1152.     }
  1153.   } else if (selector == andColonSymbol || selector == orColonSymbol) {
  1154.     if (compileAndOrStatement(selector, expr->vExpr.expression)) {
  1155.       return;
  1156.     }
  1157.   }
  1158.  
  1159.   selectorIndex = addSelector(selector);
  1160.   numArgs = listLength(expr->vExpr.expression);
  1161.  
  1162.   compileKeywordList(expr->vExpr.expression);
  1163.  
  1164.   if (expr->vExpr.receiver) {
  1165.     if (isSuper(expr->vExpr.receiver)) {
  1166.       if (selectorIndex < 0) {
  1167.     selectorIndex = addForcedSelector(selector);
  1168.       }
  1169.       hasExtendedSuper = true;
  1170.       if (selectorIndex <= 31 && numArgs <= 7) {
  1171.     compileByte(sendSuper1ExtByte);
  1172.     compileByte((numArgs << 5) | selectorIndex);
  1173.       } else {
  1174.     compileByte(sendSuper2ExtByte);
  1175.     compileByte(numArgs);
  1176.     compileByte(selectorIndex);
  1177.       }
  1178.       return;
  1179.     }
  1180.   }
  1181.  
  1182.   if (selectorIndex < 0) {
  1183.     compileByte(-selectorIndex);
  1184.   } else {
  1185.     compileSend(selectorIndex, numArgs);
  1186.   }
  1187. }
  1188.  
  1189. /*
  1190.  *    static void compileKeywordList(list)
  1191.  *
  1192.  * Description
  1193.  *
  1194.  *    Emit code to evaluate each argument to a keyword message send.
  1195.  *
  1196.  * Inputs
  1197.  *
  1198.  *    list  : A list of expressions that represents the arguments to be
  1199.  *        evaluated.  A syntax tree node.
  1200.  *
  1201.  */
  1202. static void compileKeywordList(list)
  1203. TreeNode list;
  1204. {
  1205.   for (; list; list = list->vList.next) {
  1206.     compileExpression(list->vList.value);
  1207.   }
  1208. }
  1209.  
  1210. /*
  1211.  *    static Boolean compileWhileLoop(selector, expr)
  1212.  *
  1213.  * Description
  1214.  *
  1215.  *    Special case compilation of a #whileTrue: or #whileFalse: loop.
  1216.  *
  1217.  * Inputs
  1218.  *
  1219.  *    selector: 
  1220.  *        Symbol, one of #whileTrue: or #whileFalse:.
  1221.  *    expr  : An expression that represents the entire while loop.
  1222.  *
  1223.  * Outputs
  1224.  *
  1225.  *    True if byte codes were emitted, false if not.  If either the receiver
  1226.  *    and the argument to the while message are not block expressions, this
  1227.  *    routine cannot do it's job, and so returns false to indicate as much.
  1228.  */
  1229. static Boolean compileWhileLoop(selector, expr)
  1230. OOP    selector;
  1231. TreeNode expr;
  1232. {
  1233. #ifndef old_code
  1234.   int        whileLoopLen, startLoopLen;
  1235.   ByteCodes    receiverExprCodes, whileExprCodes;
  1236.  
  1237.   if (expr->vExpr.receiver->nodeType != blockNodeType
  1238.       || expr->vExpr.expression->vList.value->nodeType != blockNodeType) {
  1239.     return (false);
  1240.   }
  1241.  
  1242.   startLoopLen = currentByteCodeLength();
  1243.  
  1244.   receiverExprCodes = compileSubExpression(expr->vExpr.receiver);
  1245.   whileExprCodes = compileSubExpression(expr->vExpr.expression->vList.value);
  1246.   compileAndFreeByteCodes(receiverExprCodes);
  1247.  
  1248.   /* skip to the while loop if the receiver block yields the proper value */
  1249.   compileJump(2, (selector == whileTrueColonSymbol) ? trueJump : falseJump);
  1250.  
  1251.   /* otherwise, skip to the end, past the pop stack top and 2 byte goto
  1252.    * and exit the loop */
  1253.   compileLongJump(byteCodeLength(whileExprCodes)+3);
  1254.  
  1255.   compileAndFreeByteCodes(whileExprCodes);
  1256.   compileByte(popStackTop);    /* we don't care about while expr's value */
  1257.  
  1258.   /* +2 since we're using a 2 byte jump instruction here, so we have to
  1259.      skip back over it in addition to the other instructions */
  1260.   whileLoopLen = currentByteCodeLength() - startLoopLen +2;
  1261.  
  1262.   compileLongJump(-whileLoopLen);
  1263.   
  1264.   /* while loops always return nil (ain't expression languages grand?) */
  1265.   compileByte(pushSpecial | nilIndex);
  1266.   return (true);
  1267. #else
  1268.   int        whileLoopLen, startLoopLen;
  1269.   ByteCodes    receiverExprCodes, whileExprCodes;
  1270.  
  1271.   if (expr->vExpr.receiver->nodeType != blockNodeType
  1272.       || expr->vExpr.expression->vList.value->nodeType != blockNodeType) {
  1273.     return (false);
  1274.   }
  1275.  
  1276.   startLoopLen = currentByteCodeLength();
  1277.  
  1278.   receiverExprCodes = compileSubExpression(expr->vExpr.receiver);
  1279.   whileExprCodes = compileSubExpression(expr->vExpr.expression->vList.value);
  1280.   compileAndFreeByteCodes(receiverExprCodes);
  1281.  
  1282.   /* skip around the while expr and the pop stack top and the 2 byte goto
  1283.      that follows if the condition doesn't match the while test. */
  1284.   compileJump(byteCodeLength(whileExprCodes)+3,
  1285.           (selector == whileTrueColonSymbol) ? falseJump : trueJump);
  1286.  
  1287.   compileAndFreeByteCodes(whileExprCodes);
  1288.   compileByte(popStackTop);    /* we don't care about while expr's value */
  1289.  
  1290.   /* +2 since we're using a 2 byte jump instruction here, so we have to
  1291.      skip back over it in addition to the other instructions */
  1292.   whileLoopLen = currentByteCodeLength() - startLoopLen +2;
  1293.   /* this is a backwards branch, but you can't tell it */
  1294.   compileByte(jumpLong | (4 - ((whileLoopLen+255)/256)));
  1295.   compileByte((-whileLoopLen) & 255);
  1296.   
  1297.   compileByte(pushSpecial | nilIndex);
  1298.   return (true);
  1299. #endif
  1300. }
  1301.  
  1302. /*
  1303.  *    static Boolean compileIfTrueFalseStatement(selector, expr)
  1304.  *
  1305.  * Description
  1306.  *
  1307.  *    Special case compile of the code for #ifTrue:false: and #ifFalse:true:
  1308.  *    messages.
  1309.  *
  1310.  * Inputs
  1311.  *
  1312.  *    selector: 
  1313.  *        Symbol, one of #ifTrue:false: or #ifFalse:true:
  1314.  *    expr  : An tree node that represents the expressions for the first and
  1315.  *        second arguments to the message.  If either is not a block type
  1316.  *        expression, no byte codes are emitted, and this routine
  1317.  *        returns false.
  1318.  *
  1319.  * Outputs
  1320.  *
  1321.  *    True if the byte codes to perform the test were successfully emitted,
  1322.  *    false if not.
  1323.  */
  1324. static Boolean compileIfTrueFalseStatement(selector, expr)
  1325. OOP    selector;
  1326. TreeNode expr;
  1327. {
  1328.   ByteCodes    trueByteCodes, falseByteCodes;
  1329.  
  1330.   if (expr->vList.value->nodeType != blockNodeType
  1331.       || expr->vList.next->vList.value->nodeType != blockNodeType) {
  1332.     return (false);
  1333.   }
  1334.  
  1335.   if (selector == ifTrueColonIfFalseColonSymbol) {
  1336.     falseByteCodes = compileSubExpression(expr->vList.next->vList.value);
  1337.     trueByteCodes =
  1338.       compileSubExpressionWithGoto(expr->vList.value,
  1339.                    byteCodeLength(falseByteCodes));
  1340.   } else {
  1341.     falseByteCodes = compileSubExpression(expr->vList.value);
  1342.     trueByteCodes =
  1343.       compileSubExpressionWithGoto(expr->vList.next->vList.value,
  1344.                    byteCodeLength(falseByteCodes));
  1345.   }
  1346.  
  1347.   compileJump(byteCodeLength(trueByteCodes), falseJump);
  1348.   compileAndFreeByteCodes(trueByteCodes);
  1349.   compileAndFreeByteCodes(falseByteCodes);
  1350.   return (true);
  1351. }
  1352.  
  1353. /*
  1354.  *    static Boolean compileIfStatement(selector, expr)
  1355.  *
  1356.  * Description
  1357.  *
  1358.  *    Special case compile of code for an #ifTrue: or #ifFalse: message.  The
  1359.  *    default value of an "if" type message is nil.
  1360.  *
  1361.  * Inputs
  1362.  *
  1363.  *    selector: 
  1364.  *        Symbol, one of #ifTrue: or #ifFalse:
  1365.  *    expr  : An expression to be evaluated if the given condition holds.
  1366.  *
  1367.  * Outputs
  1368.  *
  1369.  *    True if byte codes were emitted, false if not (as is the case if the
  1370.  *    expression following the selector is not a block).
  1371.  */
  1372. static Boolean compileIfStatement(selector, expr)
  1373. OOP    selector;
  1374. TreeNode expr;
  1375. {
  1376.   ByteCodes    thenByteCodes, defaultByteCodes;
  1377.  
  1378.   if (expr->vList.value->nodeType != blockNodeType) {
  1379.     return (false);
  1380.   }
  1381.  
  1382.   thenByteCodes = compileSubExpression(expr->vList.value);
  1383.   defaultByteCodes = compileDefaultValue(nilIndex,
  1384.                      byteCodeLength(thenByteCodes));
  1385.   compileJump(byteCodeLength(defaultByteCodes),
  1386.           (selector == ifTrueColonSymbol) ? trueJump : falseJump);
  1387.   compileAndFreeByteCodes(defaultByteCodes);
  1388.   compileAndFreeByteCodes(thenByteCodes);
  1389.   return (true);
  1390. }
  1391.  
  1392.  
  1393.  
  1394. /*
  1395.  *    static Boolean compileAndOrStatement(selector, expr)
  1396.  *
  1397.  * Description
  1398.  *
  1399.  *    Special casing for and: an or: messages.  Emits code that jumps on the
  1400.  *    condition (true for and:, false for or:) to the actual expression for
  1401.  *    the block that's the argument to the message.  Then emits code that
  1402.  *    pushes the default value onto the stack, which will be only executed in
  1403.  *    the failure cases.  Then emits the code for the evaluation of the block
  1404.  *    that's the argument to the message.
  1405.  *
  1406.  * Inputs
  1407.  *
  1408.  *    selector: 
  1409.  *        A Symbol, either #and: or #or:.
  1410.  *    expr  : A syntax tree piece that represents the expressions contained
  1411.  *        in the block that's passed as an argument of the message.
  1412.  *
  1413.  * Outputs
  1414.  *
  1415.  *    Returns true if the code was successfully emitted, and false if the
  1416.  *    code was not (such as a non-block following the selector).
  1417.  */
  1418. static Boolean compileAndOrStatement(selector, expr)
  1419. OOP    selector;
  1420. TreeNode expr;
  1421. {
  1422.   ByteCodes    blockByteCodes, defaultByteCodes;
  1423.   int        blockLen;
  1424.   
  1425.   /* I elected for simplicty sake to just emit the same kind of code always,
  1426.      and not try to save a byte by using the jump false. */
  1427.  
  1428.   if (expr->vList.value->nodeType != blockNodeType) {
  1429.     return (false);
  1430.   }
  1431.  
  1432.   blockByteCodes = compileSubExpression(expr->vList.value);
  1433.   blockLen = byteCodeLength(blockByteCodes);
  1434.   defaultByteCodes = compileDefaultValue((selector == andColonSymbol)
  1435.                      ? falseIndex : trueIndex, blockLen);
  1436.   compileJump(byteCodeLength(defaultByteCodes),
  1437.           (selector == andColonSymbol) ? trueJump : falseJump);
  1438.   compileAndFreeByteCodes(defaultByteCodes);
  1439.   compileAndFreeByteCodes(blockByteCodes);
  1440.   return (true);
  1441. }
  1442.  
  1443. /*
  1444.  *    static ByteCodes compileDefaultValue(litIndex, realExprLen)
  1445.  *
  1446.  * Description
  1447.  *
  1448.  *    Compiles and returns a byte code sequence that represents the default
  1449.  *    value of a conditional expression, such as IfTrue: when the receiver is
  1450.  *    false.  The sequence causes the default value to be pushed on the
  1451.  *    stack, and then the byte codes for the non-default value to be jumped
  1452.  *    around.
  1453.  *
  1454.  * Inputs
  1455.  *
  1456.  *    litIndex: 
  1457.  *        The index for the value to push, typcially one of true, false,
  1458.  *        or nil.  Used as part of a pushSpecial byte code.
  1459.  *    realExprLen: 
  1460.  *        A C int that represents the length of the byte codes that are
  1461.  *        evaluated in the non-default case.
  1462.  *
  1463.  * Outputs
  1464.  *
  1465.  *    A sequence of byte codes pushes the default value and skips around the
  1466.  *    computation of the real value.
  1467.  */
  1468. static ByteCodes compileDefaultValue(litIndex, realExprLen)
  1469. int    litIndex, realExprLen;
  1470. {
  1471.   ByteCodes    currentByteCodes, defaultByteCodes;
  1472.  
  1473.   currentByteCodes = saveByteCodeArray(); /* ??? don't like this name */
  1474.  
  1475.   compileByte(pushSpecial | litIndex);
  1476.   compileJump(realExprLen, unconditionalJump);
  1477.  
  1478.   defaultByteCodes = getByteCodes();
  1479.   restoreByteCodeArray(currentByteCodes);
  1480.  
  1481.   return (defaultByteCodes);
  1482. }
  1483.  
  1484. /*
  1485.  *    static ByteCodes compileSubExpression(expr)
  1486.  *
  1487.  * Description
  1488.  *
  1489.  *    Compile a "block" in a separate context and return the resulting
  1490.  *    bytecodes.  The block will not have argument declarations as it's only
  1491.  *    the code for things like ifTrue:, and:, whileTrue:, etc.  It is
  1492.  *    compiled as a list of statements such that the last statement leaves
  1493.  *    the value that is produced on the stack, as the value of the "block".
  1494.  *
  1495.  * Inputs
  1496.  *
  1497.  *    expr  : A "block" TreeNode that has no arguments. 
  1498.  *
  1499.  * Outputs
  1500.  *
  1501.  *    A ByteCodes vector of byte codes that represent the execution of the
  1502.  *    statements in the "block".
  1503.  */
  1504. static ByteCodes compileSubExpression(expr)
  1505. TreeNode expr;
  1506. {
  1507.   return (compileSubExpressionWithGoto(expr, 0));
  1508. }
  1509.  
  1510.  
  1511. /*
  1512.  *    static ByteCodes compileSubExpressionWithGoto(expr, branchLen)
  1513.  *
  1514.  * Description
  1515.  *
  1516.  *    Like compileSubExpression, except that this sub expression always ends
  1517.  *    with an unconditional branch past "branchLen" bytecodes.
  1518.  *
  1519.  * Inputs
  1520.  *
  1521.  *    expr  : a TreeNode that looks like a "block".
  1522.  *    branchLen: 
  1523.  *        number of bytes to skip over, possibly zero (in which case no
  1524.  *        goto is generated).
  1525.  *
  1526.  * Outputs
  1527.  *
  1528.  *    ByteCodes that represent the execution of the statements in "expr",
  1529.  *    with a goto after them (if "branchLen" is nonzero).
  1530.  */
  1531. static ByteCodes compileSubExpressionWithGoto(expr, branchLen)
  1532. TreeNode expr;
  1533. int    branchLen;
  1534. {
  1535.   ByteCodes    currentByteCodes, subExprByteCodes;
  1536.  
  1537.   currentByteCodes = saveByteCodeArray(); /* ??? don't like this name */
  1538.  
  1539.   compileStatements(expr->vMethod.statements, false);
  1540.   if (branchLen) {
  1541.     compileJump(branchLen, unconditionalJump);
  1542.   }
  1543.  
  1544.   subExprByteCodes = getByteCodes();
  1545.   restoreByteCodeArray(currentByteCodes);
  1546.  
  1547.   return (subExprByteCodes);
  1548. }
  1549.  
  1550. /*
  1551.  *    static void compileJump(len, jumpType)
  1552.  *
  1553.  * Description
  1554.  *
  1555.  *    Compiles a jump instruction, using the smallest possible number of byte
  1556.  *    codes.  Special cases for the unconditional jump and the short false
  1557.  *    jump that the byte code interpreter handles.
  1558.  *
  1559.  * Inputs
  1560.  *
  1561.  *    len   : Number of byte codes to jump forward (only forward jumps are
  1562.  *        handled.  A C int > 0.
  1563.  *    jumpType: 
  1564.  *        An enumerated value that indicates whether the jump is
  1565.  *        unconditional, or a true or false jump.
  1566.  *
  1567.  */
  1568. static void compileJump(len, jumpType)
  1569. int    len;
  1570. JumpType jumpType;
  1571. {
  1572.   if (len <= 0) {
  1573.     errorf("Illegal length jump %d\n", len);
  1574.     longjmp(badMethod, 1);
  1575.   }
  1576.  
  1577.   switch (jumpType) {
  1578.   case unconditionalJump:
  1579.     if (len <= 8) {
  1580.       compileByte(jumpShort | (len - 1));
  1581.     } else {
  1582.       compileByte(jumpLong | (4 + len/256));
  1583.       compileByte(len & 255);
  1584.     }
  1585.     break;
  1586.  
  1587.   case falseJump:
  1588.     if (len <= 8) {
  1589.       compileByte(popJumpFalseShort | (len - 1));
  1590.     } else {
  1591.       compileByte(popJumpFalse | (len/256));
  1592.       compileByte(len & 255);
  1593.     }
  1594.     break;
  1595.  
  1596.   case trueJump:
  1597.     compileByte(popJumpTrue | (len/256));
  1598.     compileByte(len & 255);
  1599.     break;
  1600.   }
  1601. }
  1602.  
  1603. /*
  1604.  *    compileLongJump(len)
  1605.  *
  1606.  * Description
  1607.  *
  1608.  *    Compiles an unconditional long jump, forward or backward.
  1609.  *
  1610.  * Inputs
  1611.  *
  1612.  *    len   : Number of bytes to jump.  Can be negative.
  1613.  *
  1614.  */
  1615. compileLongJump(len)
  1616. long    len;
  1617. {
  1618.   compileByte(jumpLong | (((len >> 8) + 4) & 7) );
  1619.   compileByte(len & 255);
  1620. }
  1621.  
  1622. /*
  1623.  *    compilePushIntConstant(intConst)
  1624.  *
  1625.  * Description
  1626.  *
  1627.  *    Compiles an instruction to push an Integer constant on the stack.
  1628.  *    Special cases out the literals -1..2, and tries to emit the shortest
  1629.  *    possible byte sequence to get the job done.
  1630.  *
  1631.  * Inputs
  1632.  *
  1633.  *    intConst: 
  1634.  *        The constant to be pushed; a C int.
  1635.  *
  1636.  */
  1637. compilePushIntConstant(intConst)
  1638. int    intConst;
  1639. {
  1640.   TreeNode    constExpr;
  1641.   int        constIndex;
  1642.  
  1643.   if (intConst >= -1 && intConst <= 2) {
  1644.     compileByte(pushSpecial | intConst + 5);
  1645.     return;
  1646.   }
  1647.  
  1648.   /* a hack to make use of the functionality provided by addConstant */
  1649.   constExpr = makeIntConstant((long)intConst);
  1650.   constIndex = addConstant(constExpr);
  1651.   freeTree(constExpr);
  1652.   
  1653.   if (constIndex <= 31) {
  1654.     compileByte(pushLitConstant | constIndex);
  1655.   } else {
  1656.     compileByte(pushIndexed | litConstLocation);
  1657.     compileByte(constIndex);
  1658.   }
  1659. }
  1660.  
  1661.  
  1662. /*
  1663.  *    static void compileSend(selectorIndex, numArgs)
  1664.  *
  1665.  * Description
  1666.  *
  1667.  *    Compile a message send byte code.  Tries to use the minimal length byte
  1668.  *    code sequence; does not know about the special messages that the
  1669.  *    interpreter has "wired in"; those should be handled specially and this
  1670.  *    routine should not be called with them (it's ok if it is, just not
  1671.  *    quite as efficient).
  1672.  *
  1673.  * Inputs
  1674.  *
  1675.  *    selectorIndex: 
  1676.  *        The index in the literal vector of the selector for the send
  1677.  *    numArgs: 
  1678.  *        The number of arguments that the selector takes. A C integer.
  1679.  *
  1680.  */
  1681. static void compileSend(selectorIndex, numArgs)
  1682. int    selectorIndex, numArgs;
  1683. {
  1684.   if (numArgs <= 2 && selectorIndex <= 15) {
  1685.     switch (numArgs) {
  1686.     case 0:
  1687.       compileByte(sendSelectorNoArg | selectorIndex);
  1688.       break;
  1689.     case 1:
  1690.       compileByte(sendSelector1Arg | selectorIndex);
  1691.       break;
  1692.     case 2:
  1693.       compileByte(sendSelector2Arg | selectorIndex);
  1694.       break;
  1695.     }
  1696.   } else if (selectorIndex <= 31 && numArgs <= 7) {
  1697.     compileByte(sendSelector1ExtByte);
  1698.     compileByte((numArgs << 5) | selectorIndex);
  1699.   } else {
  1700.     compileByte(sendSelector2ExtByte);
  1701.     compileByte(numArgs);
  1702.     compileByte(selectorIndex);
  1703.   }
  1704. }
  1705.  
  1706. /*
  1707.  *    static void compileCascadedMessage(cascadedExpr)
  1708.  *
  1709.  * Description
  1710.  *
  1711.  *    Compiles the code for a cascaded message send.  Due to the fact that
  1712.  *    cascaded sends go to the receiver of the last message before the first
  1713.  *    cascade "operator" (the ";"), the system to perform cascaded message
  1714.  *    sends is a bit kludgy.  We basically turn on a flag to the compiler
  1715.  *    that indicates that the value of the receiver of the last message
  1716.  *    before the cascaded sends is to be duplicated; and then compile code
  1717.  *    for each cascaded expression, throwing away the result, and duplicating
  1718.  *    the original receiver so that it can be used by the current message
  1719.  *    send, and following ones.
  1720.  *
  1721.  * Inputs
  1722.  *
  1723.  *    cascadedExpr: 
  1724.  *        A tree node that represents a cascaded expression.  Both the
  1725.  *        initial receiver and all the subsequent cascaded sends can be
  1726.  *        derived from this node.
  1727.  *
  1728.  */
  1729. static void compileCascadedMessage(cascadedExpr)
  1730. TreeNode cascadedExpr;
  1731. {
  1732.   TreeNode message;
  1733.  
  1734.   dupMessageReceiver = true;
  1735.   compileExpression(cascadedExpr->vExpr.receiver);
  1736.  
  1737.   for(message = cascadedExpr->vExpr.expression; message;
  1738.       message = message->vList.next) {
  1739.     compileByte(popStackTop);
  1740.     if (message->vList.next) {
  1741.       compileByte(dupStackTop);
  1742.     }
  1743.     compileExpression(message->vList.value);
  1744.     /* !!! remember that unary, binary and keywordexpr should ignore the
  1745.        receiver field if it is nil; that is the case for these functions
  1746.        and things work out fine if that's the case. */
  1747.   }
  1748. }
  1749.  
  1750.  
  1751. /*
  1752.  *    static void compileAssignments(varList)
  1753.  *
  1754.  * Description
  1755.  *
  1756.  *    Compiles all the assignments in "varList", which is a TreeNode of type
  1757.  *    listNode.  The generated code assumes that the value on the top of the
  1758.  *    stack is what's to be used for the assignment.  Since this routine has
  1759.  *    no notion of now the value on top of the stack will be used by the
  1760.  *    calling environment, it makes sure that when the assignments are
  1761.  *    through, that the value on top of the stack after the assignment is the
  1762.  *    same as the value on top of the stack before the assignment.  The
  1763.  *    optimizer should fix this in the unnecessary cases. 
  1764.  *
  1765.  * Inputs
  1766.  *
  1767.  *    varList: 
  1768.  *            TreeNode of type listNode that contains the names of the
  1769.  *        variables to be assigned into.
  1770.  *
  1771.  */
  1772. static void compileAssignments(varList)
  1773. TreeNode varList;
  1774. {
  1775.   SymbolEntry    variable;
  1776.   int        locationIndex;
  1777.  
  1778.   for (; varList; varList = varList->vList.next) {
  1779.     variable = findVariable(varList->vList.name);
  1780.     if (variable == nil) {
  1781.       errorf("assignment to undeclared variable %s", varList->vList.name);
  1782.       longjmp(badMethod, 1);
  1783.     }
  1784.     /* Here we have several kinds of things to store: receiver variable,
  1785.        temporary variable, "literal" variable (reference by association). */
  1786.        
  1787.     if ((variable->scope == temporaryScope
  1788.     || variable->scope == receiverScope) && variable->varIndex <= 7) {
  1789.       compileByte(dupStackTop);
  1790.       compileByte(variable->scope == temporaryScope ?
  1791.           popTemporaryVariable | variable->varIndex :
  1792.           popReceiverVariable | variable->varIndex);
  1793.     } else {
  1794.       locationIndex = computeLocationIndex(variable);
  1795.       compileByte(storeIndexed);
  1796.       compileByte(locationIndex | variable->varIndex);
  1797.     }
  1798.     freeSymbolEntry(variable);
  1799.   }
  1800. }
  1801.  
  1802. /*
  1803.  *    static int computeLocationIndex(variable)
  1804.  *
  1805.  * Description
  1806.  *
  1807.  *    Given an internal representation of a variable, this routine returns
  1808.  *    an indicator of how to access the variable.  Depending on the source of
  1809.  *    the variable, this access location (which is actually to be compiled in
  1810.  *    as part of generated byte codes) can be an instance variable in the
  1811.  *    receiver, a temporary variable in the method context (includes
  1812.  *    arguments to the method) or a global or pool variable, which are
  1813.  *    referenced by a name/value Association.
  1814.  *
  1815.  * Inputs
  1816.  *
  1817.  *    variable: 
  1818.  *        Internal compiler variable.
  1819.  *
  1820.  * Outputs
  1821.  *
  1822.  *    Indication of how to access the variable.  A portion of a byte code
  1823.  *    that represents the various types of storage locations.
  1824.  */
  1825. static int computeLocationIndex(variable)
  1826. SymbolEntry variable;
  1827. {
  1828.   switch (variable->scope) {
  1829.   case receiverScope:
  1830.     return (receiverLocation);
  1831.   case temporaryScope:
  1832.     return (temporaryLocation);
  1833.   case globalScope: case poolScope:
  1834.     return (litVarLocation);
  1835.   }
  1836.  
  1837.   errorf("Unhandled case in computeLocationIndex\n");
  1838.   return (0);
  1839. }
  1840.  
  1841. /*
  1842.  *    static int isSpecialVariable(expr)
  1843.  *
  1844.  * Description
  1845.  *
  1846.  *    Examines the expression "expr" to see if it is a variable in the set
  1847.  *    "self", "false", "true", "nil".  Returns the "index" (number in 0..3)
  1848.  *    if true, -1 if false.  For this use, "super" is defined to be the
  1849.  *    equivalent of self.
  1850.  *
  1851.  * Inputs
  1852.  *
  1853.  *    expr  : TreeNode expression to be examined
  1854.  *
  1855.  * Outputs
  1856.  *
  1857.  *    -1 if expr is not a special variable
  1858.  *    0..3 if expr is "self", "true", "false", "nil", respectively.
  1859.  *    "super" is the same as self.
  1860.  */
  1861. static int isSpecialVariable(expr)
  1862. TreeNode expr;
  1863. {
  1864.   OOP        variable;
  1865.  
  1866.   if (expr->nodeType != variableNodeType) {
  1867.     return (-1);
  1868.   }
  1869.  
  1870.   variable = internString(expr->vList.name);
  1871.   if (variable == selfSymbol || variable == superSymbol) {
  1872.     return (receiverIndex);
  1873.   } else if (variable == trueSymbol) {
  1874.     return (trueIndex);
  1875.   } else if (variable == falseSymbol) {
  1876.     return (falseIndex);
  1877.   } else if (variable == nilSymbol) {
  1878.     return (nilIndex);
  1879.   } else {
  1880.     return (-1);
  1881.   }
  1882. }
  1883.  
  1884.  
  1885. /*
  1886.  *    static Boolean isSuper(expr)
  1887.  *
  1888.  * Description
  1889.  *
  1890.  *    Returns true if the expression passed to it represents the symbol
  1891.  *    "super"; false if not.
  1892.  *
  1893.  * Inputs
  1894.  *
  1895.  *    expr  : TreeNode that is an expression of some kind
  1896.  *
  1897.  * Outputs
  1898.  *
  1899.  *    true if "super" variable, false otherwise.
  1900.  */
  1901. static Boolean isSuper(expr)
  1902. TreeNode expr;
  1903. {
  1904.   if (expr->nodeType != variableNodeType) {
  1905.     return (false);
  1906.   }
  1907.  
  1908.   return (internString(expr->vList.name) == superSymbol);
  1909. }
  1910.  
  1911.  
  1912. /*
  1913.  *    static int addConstant(constExpr)
  1914.  *
  1915.  * Description
  1916.  *
  1917.  *    Scans the constants that are referenced by the current method.  If one
  1918.  *    is found that is equal to constExpr, the index of that constant is
  1919.  *    returned.  Otherwise, the constant is turned into a constant object,
  1920.  *    added to the constants of the method, and the new index is returned.
  1921.  *
  1922.  * Inputs
  1923.  *
  1924.  *    constExpr: 
  1925.  *        TreeNode of type constExprType, containing a literal constant
  1926.  *        of some kind.  Special cases -1, 0, 1, and 2 since they are
  1927.  *        available directly via instructions.
  1928.  *
  1929.  * Outputs
  1930.  *
  1931.  *    Index in the method's table of where this constant can be found,
  1932.  *    whether or not it was already there.  Returns a negative number if
  1933.  *    the constant is one of the above mentioned integers; in fact, the value
  1934.  *    returned is such that 3+returnValue is the numerical value.
  1935.  */
  1936. static int addConstant(constExpr)
  1937. TreeNode constExpr;
  1938. {
  1939.   int        i;
  1940.   long        intVal;
  1941.   OOP        constantOOP;
  1942.  
  1943.   for (i = 0; i < numLiterals; i++) {
  1944.     if (equalConstant(literalVec[i], constExpr)) {
  1945.       return (i);
  1946.     }
  1947.   }
  1948.  
  1949.   constantOOP = makeConstantOOP(constExpr);
  1950.   if (isInt(constantOOP)) {
  1951.     intVal = toInt(constantOOP);
  1952.     if (intVal >= -1 && intVal <= 2) {
  1953.       return ((int)(intVal - 3));
  1954.     }
  1955.   }
  1956.  
  1957.   return (addLiteral(constantOOP));
  1958. }
  1959.  
  1960. /*
  1961.  *    static Boolean equalConstant(oop, constExpr)
  1962.  *
  1963.  * Description
  1964.  *
  1965.  *    Returns true if "oop" and "constExpr" represent the same literal value.
  1966.  *    Primarily used by the compiler to store a single copy of duplicated
  1967.  *    literals in a method.  Can call itself in the case array literals.
  1968.  *
  1969.  * Inputs
  1970.  *
  1971.  *    oop   : An OOP that represents a constant value
  1972.  *    constExpr: 
  1973.  *        A piece of the syntax tree that represents a literal value.
  1974.  *
  1975.  * Outputs
  1976.  *
  1977.  *    True if "oop" and "constExpr" represent the same value; false
  1978.  *    otherwise. 
  1979.  */
  1980. static Boolean equalConstant(oop, constExpr)
  1981. OOP    oop;
  1982. TreeNode constExpr;
  1983. {
  1984.   TreeNode    arrayElt;
  1985.   int        len, i;
  1986.  
  1987.   /* ??? this kind of special casing of the elements of arrays bothers
  1988.      me...it should all be in one neat place. */
  1989.   if (constExpr->nodeType == symbolNodeType) { /* symbol in array constant */
  1990.     return (oop == constExpr->vExpr.selector);
  1991.   } else if (constExpr->nodeType == arrayEltListType) {
  1992.     if (isOOP(oop) && oopToObj(oop)->objClass == arrayClass) {
  1993.       for(len = 0, arrayElt = constExpr; arrayElt;
  1994.       len++, arrayElt = arrayElt->vList.next);
  1995.  
  1996.       if (len == numOOPs(oopToObj(oop))) {
  1997.     for (i = 1, arrayElt = constExpr; i <= len;
  1998.          i++, arrayElt = arrayElt->vList.next) {
  1999.       if (!equalConstant(arrayAt(oop, i), arrayElt->vList.value)) {
  2000.         return (false);
  2001.       }
  2002.     }
  2003.     return (true);
  2004.       }
  2005.     }
  2006.     return (false);
  2007.   }
  2008.  
  2009.  
  2010.  
  2011.   switch (constExpr->vConst.constType) {
  2012.   case intConst:
  2013.     if (oop == fromInt(constExpr->vConst.val.iVal)) {
  2014.       return (true);
  2015.     }
  2016.     break;
  2017.  
  2018.   case floatConst:
  2019.     if (isOOP(oop) && oopToObj(oop)->objClass == floatClass) {
  2020.       if (constExpr->vConst.val.fVal == floatOOPValue(oop)) {
  2021.     return (true);
  2022.       }
  2023.     }
  2024.     break;
  2025.  
  2026.   case charConst:
  2027.     if (oop == charOOPAt(constExpr->vConst.val.cVal)) {
  2028.       return (true);
  2029.     }
  2030.     break;
  2031.  
  2032.   case stringConst:
  2033.     if (isOOP(oop) && oopToObj(oop)->objClass == stringClass) {
  2034.       len = strlen(constExpr->vConst.val.sVal);
  2035.       if (len == stringOOPLen(oop)) {
  2036.     if (strncmp((char *)oopToObj(oop)->data, constExpr->vConst.val.sVal,
  2037.             len) == 0) {
  2038.       return (true);
  2039.     }
  2040.       }
  2041.     }
  2042.     break;
  2043.  
  2044.   case symbolConst:
  2045.     if (oop == constExpr->vConst.val.symVal) {
  2046.       return (true);
  2047.     }
  2048.     break;
  2049.     
  2050.   case arrayConst:
  2051.     if (isOOP(oop) && oopToObj(oop)->objClass == arrayClass) {
  2052.       /* ??? could keep the length in a counter */
  2053.       for(len = 0, arrayElt = constExpr->vConst.val.aVal; arrayElt;
  2054.       len++, arrayElt = arrayElt->vList.next);
  2055.       if (len == numOOPs(oopToObj(oop))) {
  2056.     for (i = 1, arrayElt = constExpr->vConst.val.aVal; i <= len;
  2057.          i++, arrayElt = arrayElt->vList.next) {
  2058.       if (!equalConstant(arrayAt(oop, i), arrayElt->vList.value)) {
  2059.         return (false);
  2060.       }
  2061. #ifdef bogus /* Sat Feb 16 14:30:37 1991 */
  2062. /**/      if (!equalConstant(arrayElt->vList.value, arrayAt(oop, i))) {
  2063. /**/        return (false);
  2064. /**/      }
  2065. #endif /* bogus Sat Feb 16 14:30:37 1991 */
  2066.     }
  2067.     return (true);
  2068.       }
  2069.     }
  2070.     break;
  2071.   }
  2072.  
  2073.   return (false);
  2074. }
  2075.  
  2076. /*
  2077.  *    static OOP makeConstantOOP(constExpr)
  2078.  *
  2079.  * Description
  2080.  *
  2081.  *    Given a section of the syntax tree that represents a Smalltalk
  2082.  *    constant, this routine creates an OOP to be stored as a method literal
  2083.  *    in the method that's currently being compiled.
  2084.  *
  2085.  * Inputs
  2086.  *
  2087.  *    constExpr: 
  2088.  *        A portion of the tree that contains a constant value, including
  2089.  *        array constants.
  2090.  *
  2091.  * Outputs
  2092.  *
  2093.  *    An OOP that represents the constant's value.
  2094.  */
  2095. static OOP makeConstantOOP(constExpr)
  2096. TreeNode constExpr;
  2097. {
  2098.   TreeNode    arrayElt;
  2099.   int        len, i;
  2100.   OOP        resultOOP;
  2101.  
  2102.   if (constExpr->nodeType == symbolNodeType) { /* symbol in array constant */
  2103.     return (constExpr->vExpr.selector);
  2104.   } else if (constExpr->nodeType == arrayEltListType) {
  2105.     for(len = 0, arrayElt = constExpr; arrayElt;
  2106.     len++, arrayElt = arrayElt->vList.next);
  2107.     
  2108.     /* ??? this might be an uninitialized form of array creation for speed */
  2109.     resultOOP = arrayNew(len);
  2110.  
  2111.     for (i = 1, arrayElt = constExpr; i <= len;
  2112.      i++, arrayElt = arrayElt->vList.next) {
  2113.       arrayAtPut(resultOOP, i, makeConstantOOP(arrayElt->vList.value));
  2114.     }
  2115.     return (resultOOP);
  2116.   }
  2117.  
  2118.   switch (constExpr->vConst.constType) {
  2119.   case intConst:
  2120.     return (fromInt(constExpr->vConst.val.iVal));
  2121.  
  2122.   case floatConst:
  2123.     return (floatNew(constExpr->vConst.val.fVal));
  2124.  
  2125.   case charConst:
  2126.     return (charOOPAt(constExpr->vConst.val.cVal));
  2127.  
  2128.   case stringConst:
  2129.     return (stringNew(constExpr->vConst.val.sVal));
  2130.  
  2131.   case symbolConst:
  2132.     return (constExpr->vConst.val.symVal);
  2133.     
  2134.   case arrayConst:
  2135.     for(len = 0, arrayElt = constExpr->vConst.val.aVal; arrayElt;
  2136.     len++, arrayElt = arrayElt->vList.next);
  2137.     
  2138.     /* ??? this might be an uninitialized form of array creation for speed */
  2139.     resultOOP = arrayNew(len);
  2140.  
  2141.     for (i = 1, arrayElt = constExpr->vConst.val.aVal; i <= len;
  2142.      i++, arrayElt = arrayElt->vList.next) {
  2143.       arrayAtPut(resultOOP, i, makeConstantOOP(arrayElt->vList.value));
  2144.     }
  2145.     return (resultOOP);
  2146.   }
  2147.  
  2148.   return (nilOOP);
  2149. }
  2150.  
  2151. /*
  2152.  *    static int addSelector(selector)
  2153.  *
  2154.  * Description
  2155.  *
  2156.  *    Like addConstant, this routine adds "selector" to the set of selectors
  2157.  *    for the current method, and returns the index of that selector.  If the
  2158.  *    selector already existed, its index is returned.  If the selector is
  2159.  *    a special selector, then the negative of the bytecode that's associated
  2160.  *    with that special selector is returned.
  2161.  *
  2162.  * Inputs
  2163.  *
  2164.  *    selector: 
  2165.  *        A symbol that is the selector to be added to the selectors of
  2166.  *        the current method.
  2167.  *
  2168.  * Outputs
  2169.  *
  2170.  *    Index of the selector in the current method, or number < 0 which is
  2171.  *    the negative of the bytecode for a send special.
  2172.  */
  2173. static int addSelector(selector)
  2174. OOP    selector;
  2175. {
  2176.   int        builtin;
  2177.  
  2178.   if ((builtin = whichBuiltinSelector(selector)) != 0) {
  2179.     return (-builtin);
  2180.   } else {
  2181.     return (addForcedSelector(selector));
  2182.   }
  2183. }
  2184.  
  2185. /*
  2186.  *    static int whichBuiltinSelector(selector)
  2187.  *
  2188.  * Description
  2189.  *
  2190.  *    Looks for special-cased selectors, and returns a special number to
  2191.  *    indicate which selector was chosen.  If the selector isn't one of the
  2192.  *    special-cased ones, 0 is returned.
  2193.  *
  2194.  * Inputs
  2195.  *
  2196.  *    selector: 
  2197.  *        An instance of Symbol, to be special-cased.
  2198.  *
  2199.  * Outputs
  2200.  *
  2201.  *    0 if the selector isn't special-cased, otherwise an index to be used
  2202.  *    by the compiler for the selector.
  2203.  */
  2204. static int whichBuiltinSelector(selector)
  2205. OOP    selector;
  2206. {
  2207.   if (selector == atColonSymbol) {
  2208.     return (atColonSpecial);
  2209.   } else if (selector == atColonPutColonSymbol) {
  2210.     return (atColonPutColonSpecial);
  2211.   } else if (selector == sizeSymbol) {
  2212.     return (sizeSpecial);
  2213.   } else if (selector == nextSymbol) {
  2214.     return (nextSpecial);
  2215.   } else if (selector == nextPutColonSymbol) {
  2216.     return (nextPutColonSpecial);
  2217.   } else if (selector == atEndSymbol) {
  2218.     return (atEndSpecial);
  2219.   } else if (selector == classSymbol) {
  2220.     return (classSpecial);
  2221.   } else if (selector == blockCopyColonSymbol) {
  2222.     return (blockCopyColonSpecial);
  2223.   } else if (selector == valueSymbol) {
  2224.     return (valueSpecial);
  2225.   } else if (selector == valueColonSymbol) {
  2226.     return (valueColonSpecial);
  2227.   } else if (selector == doColonSymbol) {
  2228.     return (doColonSpecial);
  2229.   } else if (selector == newSymbol) {
  2230.     return (newSpecial);
  2231.   } else if (selector == newColonSymbol) {
  2232.     return (newColonSpecial);
  2233.   } else if (selector == plusSymbol) {
  2234.     return (plusSpecial);
  2235.   } else if (selector == minusSymbol) {
  2236.     return (minusSpecial);
  2237.   } else if (selector == lessThanSymbol) {
  2238.     return (lessThanSpecial);
  2239.   } else if (selector == greaterThanSymbol) {
  2240.     return (greaterThanSpecial);
  2241.   } else if (selector == lessEqualSymbol) {
  2242.     return (lessEqualSpecial);
  2243.   } else if (selector == greaterEqualSymbol) {
  2244.     return (greaterEqualSpecial);
  2245.   } else if (selector == equalSymbol) {
  2246.     return (equalSpecial);
  2247.   } else if (selector == notEqualSymbol) {
  2248.     return (notEqualSpecial);
  2249.   } else if (selector == timesSymbol) {
  2250.     return (timesSpecial);
  2251.   } else if (selector == divideSymbol) {
  2252.     return (divideSpecial);
  2253.   } else if (selector == remainderSymbol) {
  2254.     return (remainderSpecial);
  2255.   } else if (selector == bitShiftColonSymbol) {
  2256.     return (bitShiftColonSpecial);
  2257.   } else if (selector == integerDivideSymbol) {
  2258.     return (integerDivideSpecial);
  2259.   } else if (selector == bitAndColonSymbol) {
  2260.     return (bitAndColonSpecial);
  2261.   } else if (selector == bitOrColonSymbol) {
  2262.     return (bitOrColonSpecial);
  2263.   } else if (selector == sameObjectSymbol) {
  2264.     return (sameObjectSpecial);
  2265.   } else {
  2266.     return (0);
  2267.   }
  2268. }
  2269.  
  2270.  
  2271. /*
  2272.  *    static int addForcedSelector(selector)
  2273.  *
  2274.  * Description
  2275.  *
  2276.  *    Adds the given selector to the method literals, returning the index
  2277.  *    that the selector was stored under.
  2278.  *
  2279.  * Inputs
  2280.  *
  2281.  *    selector: 
  2282.  *        An instance of Symbol to be added.
  2283.  *
  2284.  * Outputs
  2285.  *
  2286.  *    Index of where in the literal vector the Symbol was stored.
  2287.  */
  2288. static int addForcedSelector(selector)     
  2289. OOP    selector;
  2290. {
  2291.  
  2292.   return (addForcedObject(selector));
  2293. }
  2294.  
  2295. /*
  2296.  *    int addForcedObject(oop)
  2297.  *
  2298.  * Description
  2299.  *
  2300.  *    Adds "oop" to the literal vector that's being created, unless it's
  2301.  *    already there.  "Already there" is defined as the exact same object is
  2302.  *    present in the literal vector.
  2303.  *
  2304.  * Inputs
  2305.  *
  2306.  *    oop   : An OOP to be added to the literal vector.
  2307.  *
  2308.  * Outputs
  2309.  *
  2310.  *    Index into the literal vector where the object was stored.  Seems like
  2311.  *    it's zero based, but I believe in practice that it's 1 based.
  2312.  */
  2313. int addForcedObject(oop)
  2314. OOP    oop;
  2315. {
  2316.   int        i;
  2317.  
  2318.   for (i = 0; i < numLiterals; i++) {
  2319.     if (literalVec[i] == oop) {
  2320.       return (i);
  2321.     }
  2322.   }
  2323.  
  2324.   return (addLiteral(oop));
  2325. }
  2326.  
  2327. /*
  2328.  *    static OOP computeSelector(selectorExpr)
  2329.  *
  2330.  * Description
  2331.  *
  2332.  *    Given a TreeNode of type keywordExprNode, this routine picks out the
  2333.  *    names selector "keywords", concatenates them, turns them into a symbol
  2334.  *    and returns that symbol.  This routine may also be called with a
  2335.  *    unaryExprType or binaryExprType tree node, in which case the selector
  2336.  *    is already known, so it is just returned.
  2337.  *
  2338.  * Inputs
  2339.  *
  2340.  *    selectorExpr:
  2341.  *        TreeNode node of type keywordExprNode, unaryExprType, or
  2342.  *        binaryExprType.
  2343.  *
  2344.  * Outputs
  2345.  *
  2346.  *    Symbol OOP that is the selector that "selectorExpr" represents.
  2347.  */
  2348. static OOP computeSelector(selectorExpr)
  2349. TreeNode selectorExpr;
  2350. {
  2351.   TreeNode    keyword;
  2352.   int        len;
  2353.   char        *nameBuf, *p;
  2354.  
  2355.   if (selectorExpr->nodeType == unaryExprType
  2356.       || selectorExpr->nodeType == binaryExprType) {
  2357.     return (selectorExpr->vExpr.selector);
  2358.   }
  2359.  
  2360.   len = 0;
  2361.   for (keyword = selectorExpr->vExpr.expression; keyword != nil;
  2362.        keyword = keyword->vList.next) {
  2363.     len += strlen(keyword->vList.name);
  2364.   }
  2365.  
  2366.   p = nameBuf = (char *)alloca(len+1);
  2367.   for (keyword = selectorExpr->vExpr.expression; keyword != nil;
  2368.        keyword = keyword->vList.next) {
  2369.     len = strlen(keyword->vList.name);
  2370.     strcpy(p, keyword->vList.name);
  2371.     p += len;
  2372.   }
  2373.  
  2374.   *p = '\0';
  2375.  
  2376.   return (internString(nameBuf));
  2377. }
  2378.  
  2379.  
  2380. /*
  2381.  *    static void addMethodClassVariable()
  2382.  *
  2383.  * Description
  2384.  *
  2385.  *    Called when a method contains at least one reference to super as a
  2386.  *    receiver, this routine makes sure that the last literal variable of the
  2387.  *    method is the class that the method is a part of.
  2388.  *
  2389.  */
  2390. static void addMethodClassVariable()
  2391. {
  2392.   addLiteral(associationNew(getClassSymbol(thisClass), thisClass));
  2393. }
  2394.  
  2395.  
  2396. /*
  2397.  *    initCompiler()
  2398.  *
  2399.  * Description
  2400.  *
  2401.  *    Prepares the compiler for execution.
  2402.  *
  2403.  */
  2404. initCompiler()
  2405. {
  2406.   hasExtendedSuper = false;
  2407.   initArgCount();
  2408.   initTempCount();
  2409.   initLiteralVec();
  2410.   initByteCodes();
  2411. }
  2412.  
  2413. /*
  2414.  *    static int listLength(listExpr)
  2415.  *
  2416.  * Description
  2417.  *
  2418.  *    Computes and returns the length of a parse tree list.
  2419.  *
  2420.  * Inputs
  2421.  *
  2422.  *    listExpr: 
  2423.  *        A list from the tree builder.
  2424.  *
  2425.  * Outputs
  2426.  *
  2427.  *    The length of the list, as an integer.
  2428.  */
  2429. static int listLength(listExpr)
  2430. TreeNode listExpr;
  2431. {
  2432.   TreeNode     l;
  2433.   long        len;
  2434.  
  2435.   for(len = 0, l = listExpr; l; l = l->vList.next, len++);
  2436.  
  2437.   if (sizeof(int) != 4) {
  2438.     if (len > (1L << (sizeof(int)*8 - 1))) {
  2439.       errorf("List too long, %ld", len);
  2440.       len = 1L << (sizeof(int)*8 - 1);
  2441.     }
  2442.   }
  2443.  
  2444.   return ((int)len);
  2445. }
  2446.  
  2447. /*
  2448.  *    static ByteCodes optimizeByteCodes(byteCodes)
  2449.  *
  2450.  * Description
  2451.  *
  2452.  *    Intended to scan the byte codes of a method, performing optimizations,
  2453.  *    and return a new vector of byte codes that represents the optimized
  2454.  *    byte code stream.  Currently a NOP.
  2455.  *
  2456.  * Inputs
  2457.  *
  2458.  *    byteCodes: 
  2459.  *        A vector of byte codes to be optimized.
  2460.  *
  2461.  * Outputs
  2462.  *
  2463.  *    Currently, the same byte codes that were passed in.
  2464.  */
  2465. static ByteCodes optimizeByteCodes(byteCodes)
  2466. ByteCodes byteCodes;
  2467. {
  2468.   /* ??? no optimization for now */
  2469.   return (byteCodes);
  2470. }
  2471.  
  2472.  
  2473. /***********************************************************************
  2474.  *
  2475.  *    Literal Vector manipulation routines.
  2476.  *
  2477.  ***********************************************************************/
  2478.  
  2479.  
  2480. /*
  2481.  *    static void initLiteralVec()
  2482.  *
  2483.  * Description
  2484.  *
  2485.  *    Prepares the literal vector for use.  The literal vector is where the
  2486.  *    compiler will store any literals that are used by the method being
  2487.  *    compiled.  The literal vector will grow as needed to accomodate the
  2488.  *    literals of the method.
  2489.  *
  2490.  */
  2491. static void initLiteralVec()
  2492. {
  2493.   numLiterals = 0;
  2494.  
  2495.   literalVecMax = LITERAL_VEC_CHUNK_SIZE;
  2496.   literalVec = (OOP *)malloc(LITERAL_VEC_CHUNK_SIZE*sizeof(OOP));
  2497. }
  2498.  
  2499. /*
  2500.  *    static int addLiteral(OOP)
  2501.  *
  2502.  * Description
  2503.  *
  2504.  *    Adds "OOP" to the literals associated with the method being compiled
  2505.  *    and returns the index of the literal slot that was used (1 based).
  2506.  *
  2507.  * Inputs
  2508.  *
  2509.  *    oop:    OOP to add to the literal vector.
  2510.  *
  2511.  * Outputs
  2512.  *
  2513.  *    Index (1 based) in the literal vector of where the OOP was added.
  2514.  */
  2515. static int addLiteral(oop)
  2516. OOP    oop;
  2517. {
  2518.   if (numLiterals >= literalVecMax) {
  2519.     reallocLiteralVec();
  2520.   }
  2521.  
  2522.   literalVec[numLiterals] = oop;
  2523.   return (numLiterals++);
  2524. }
  2525.  
  2526. /*
  2527.  *    static void reallocLiteralVec()
  2528.  *
  2529.  * Description
  2530.  *
  2531.  *    Called to grow the literal vector that the compiler is using.  Modifies
  2532.  *    the global variables "literalVec" and "literalVecMax" to reflect the
  2533.  *    growth. 
  2534.  *
  2535.  */
  2536. static void reallocLiteralVec()
  2537. {
  2538.   literalVecMax += LITERAL_VEC_CHUNK_SIZE;
  2539.   literalVec = (OOP *)realloc(literalVec, literalVecMax * sizeof(OOP));
  2540. }
  2541.  
  2542.  
  2543. /*
  2544.  *    OOP getMethodLiterals()
  2545.  *
  2546.  * Description
  2547.  *
  2548.  *    Creates a new array object that contains the literals for the method
  2549.  *    that's being compiled and returns it.  As a side effect, the currently
  2550.  *    allocated working literal vector is freed.  If there were no literals
  2551.  *    for the current method, nilOOP is returned.
  2552.  *
  2553.  * Outputs
  2554.  *
  2555.  *    The newly created array object, or nilOOP.
  2556.  */
  2557. static OOP getMethodLiterals()
  2558. {
  2559.   OOP        methodLiterals;
  2560.   int        i;
  2561.  
  2562.   if (numLiterals == 0) {
  2563.     return (nilOOP);
  2564.   }
  2565.  
  2566.   if (numLiterals > MAX_NUM_LITERALS) {
  2567.     errorf("Maximum number of literals, %d, exceeded: %d.  Extras ignored",
  2568.        MAX_NUM_LITERALS, numLiterals);
  2569.     numLiterals = MAX_NUM_LITERALS;
  2570.     hadError = true;
  2571.   }
  2572.  
  2573.   methodLiterals = arrayNew(numLiterals);
  2574.   for (i = 0; i < numLiterals; i++) {
  2575.     arrayAtPut(methodLiterals, i+1, literalVec[i]);
  2576.   }
  2577.  
  2578.   free(literalVec);
  2579.  
  2580.   return (methodLiterals);
  2581. }
  2582.  
  2583. /*
  2584.  *    static void installMethod(selector, primitiveIndex, numArgs, numTemps, literals, byteCodes)
  2585.  *
  2586.  * Description
  2587.  *
  2588.  *    Creates a new CompileMethod and installs it in the method dictionary
  2589.  *    for the current class.  If the current class does not contain a valid
  2590.  *    method dictionary, one is allocated for it.
  2591.  *
  2592.  * Inputs
  2593.  *
  2594.  *    selector: 
  2595.  *        A symbol that the compiled method should be stored under.  This
  2596.  *        selector is what will be matched against the message selector
  2597.  *        when a message is sent to the class that this method is a part
  2598.  *        of.
  2599.  *    primitiveIndex: 
  2600.  *        A C integer for the primitive operation associated with this
  2601.  *        method.  0 if no primitive is associated with this method.
  2602.  *    numArgs: 
  2603.  *        A C integer for the number of arguments that this method has.
  2604.  *    numTemps: 
  2605.  *        A C integer for the number of temporaries that this method has.
  2606.  *    literals: 
  2607.  *        An Array of method literals.
  2608.  *    byteCodes: 
  2609.  *        Vector of the bytecodes for the CompiledMethod.
  2610.  *
  2611.  */
  2612. static void installMethod(selector, primitiveIndex, numArgs, numTemps,
  2613.               literals, byteCodes)
  2614. OOP    selector, literals;
  2615. int    primitiveIndex, numArgs, numTemps;
  2616. ByteCodes byteCodes;
  2617. {
  2618.   OOP        method, methodDictionaryOOP;
  2619.  
  2620.   if (declareTracing) {
  2621.     printf("Class "); printObject(thisClass); printf("\n");
  2622.     printf("   "); printSymbol(selector); printf("\n");
  2623.   }
  2624.  
  2625.   methodDictionaryOOP = validClassMethodDictionary(thisClass);
  2626.   method = makeNewMethod(primitiveIndex, numArgs, numTemps, literals,
  2627.              byteCodes);
  2628.   latestCompiledMethod = method;
  2629.  
  2630.   identityDictionaryAtPut(methodDictionaryOOP, selector, method); 
  2631.   updateMethodCache(selector, thisClass, method);
  2632. }
  2633.  
  2634. /*
  2635.  *    static OOP makeNewMethod(primitiveIndex, numArgs, numTemps, literals, byteCodes)
  2636.  *
  2637.  * Description
  2638.  *
  2639.  *    Constructs and returns a new CompiledMethod instance.  It computes the
  2640.  *    method header based on its arguments, and on the contents of the
  2641.  *    method's byte codes.
  2642.  *
  2643.  * Inputs
  2644.  *
  2645.  *    primitiveIndex: 
  2646.  *        Integer, non-zero indicates that the method has a primitive
  2647.  *        method associated with it.  The primitive will be invoked first
  2648.  *        when the method is invoked, and only if the primitive signals
  2649.  *        failure will the remaining byte codes of the method be
  2650.  *        executed.
  2651.  *    numArgs: 
  2652.  *        Number of arguments that the method has.  A C integer.
  2653.  *    numTemps: 
  2654.  *        Number of temporaries that the method has. C integer.
  2655.  *    literals: 
  2656.  *        An Array of method literals that the CompiledMethod should use.
  2657.  *    byteCodes: 
  2658.  *        A vector of byte codes that make up the executable part of the
  2659.  *        method. 
  2660.  *
  2661.  * Outputs
  2662.  *
  2663.  *    A newly allocated CompiledMethod, fully initialized and ready to go.
  2664.  */
  2665. static OOP makeNewMethod(primitiveIndex, numArgs, numTemps, literals,
  2666.              byteCodes)
  2667. int    primitiveIndex, numArgs, numTemps;
  2668. OOP    literals;
  2669. ByteCodes byteCodes;
  2670. {
  2671.   MethodHeader    header;
  2672.   int        newFlags;
  2673.  
  2674.   header.intMark = 1;
  2675.   header.headerFlag = 0;
  2676.   if (primitiveIndex) {
  2677.     header.headerFlag = 3;
  2678.     if (declareTracing) {
  2679.       printf("  Primitive Index %d\n", primitiveIndex);
  2680.     }
  2681.   }
  2682.  
  2683.   header.primitiveIndex = primitiveIndex;
  2684.   header.numArgs = numArgs;
  2685.   header.numTemps = numTemps;
  2686.   header.numLiterals = numLiterals;
  2687.  
  2688.   if (primitiveIndex == 0) {
  2689.     if (numArgs == 0 && numTemps == 0
  2690.     && (newFlags = isSimpleReturn(byteCodes)) != 0) {
  2691.       header.headerFlag = newFlags & 0xFF;
  2692.       if (header.headerFlag == 2) {
  2693.     /* if returning an instance variable, this is indicated in the number
  2694.        of temporary variables */
  2695.     header.numTemps = newFlags >> 8;
  2696.       }
  2697.       freeByteCodes(byteCodes);
  2698.       byteCodes = nil;
  2699.       literals = nilOOP;
  2700.     }
  2701.   }
  2702.   return (methodNew(header, literals, byteCodes));
  2703. }
  2704.  
  2705. /*
  2706.  *    static OOP methodNew(header, literals, byteCodes)
  2707.  *
  2708.  * Description
  2709.  *
  2710.  *    Creates and returns a CompiledMethod.  The method is completely filled
  2711.  *    in, including the descriptor, the method literals, and the byte codes
  2712.  *    for the method.
  2713.  *
  2714.  * Inputs
  2715.  *
  2716.  *    header: Header of the method, a Smalltalk Integer.
  2717.  *    literals: 
  2718.  *        A Smalltalk Array of literals that the method should contain.
  2719.  *    byteCodes: 
  2720.  *        The byte code vector for the method.
  2721.  *
  2722.  * Outputs
  2723.  *
  2724.  *    A newly created CompiledMethod instance that's completely initialized.
  2725.  */
  2726. static OOP methodNew(header, literals, byteCodes)
  2727. MethodHeader header;
  2728. OOP    literals;
  2729. ByteCodes byteCodes;
  2730. {
  2731.   int        numByteCodes, numLiterals, i;
  2732.   CompiledMethod method;
  2733.   OOP        litOOP, methodOOP;
  2734.  
  2735.   if (byteCodes != nil) {
  2736.     numByteCodes = byteCodeLength(byteCodes);
  2737.   } else {
  2738.     numByteCodes = 0;
  2739.   }
  2740.  
  2741.   method = simpleMethodNew(numByteCodes, header);
  2742.   method->descriptor = methodInfoNew();
  2743.   maybeMoveOOP(method->descriptor);
  2744.  
  2745.   numLiterals = header.numLiterals;
  2746.  
  2747.   for (i = 1; i <= numLiterals; i++) {
  2748.     litOOP = arrayAt(literals, i);
  2749.     maybeMoveOOP(litOOP);
  2750.     method->literals[i-1] = litOOP;
  2751.   }
  2752.  
  2753.   if (byteCodes != nil) {
  2754.     copyByteCodes((Byte *)&method->literals[numLiterals], byteCodes);
  2755.   }
  2756.  
  2757.   if (declareTracing) {
  2758.     printByteCodes(byteCodes, method->literals);
  2759.   }
  2760.  
  2761.   freeByteCodes(byteCodes);
  2762.  
  2763.   methodOOP = allocOOP(method);
  2764.   methodOOP->flags |= (4 - numByteCodes) & 3;
  2765.   return (methodOOP);
  2766. }
  2767.  
  2768. /*
  2769.  *    OOP methodNewOOP(numByteCodes, header)
  2770.  *
  2771.  * Description
  2772.  *
  2773.  *    Used to implement the primitive that creates new compiled methods.
  2774.  *    Allocates and returns an OOP for a CompiledMethod.
  2775.  *
  2776.  * Inputs
  2777.  *
  2778.  *    numByteCodes: 
  2779.  *        Number of byte codes that the CompiledMethod will contain.
  2780.  *    header: The header for the CompiledMethod.  A Smalltalk integer.
  2781.  *
  2782.  * Outputs
  2783.  *
  2784.  *    Newly allocated CompiledMethod OOP.
  2785.  */
  2786. OOP methodNewOOP(numByteCodes, header)
  2787. long    numByteCodes;
  2788. MethodHeader header;
  2789. {
  2790.   CompiledMethod method;
  2791.   OOP        oop;
  2792.  
  2793.   method = simpleMethodNew(numByteCodes, header);
  2794.   oop = allocOOP(method);
  2795.   /* ### Perhaps this could be a little better abstracted */
  2796.   oop->flags |= (4 - numByteCodes) & 3;
  2797.  
  2798.   return (oop);
  2799. }
  2800.  
  2801. /*
  2802.  *    static CompiledMethod simpleMethodNew(numByteCodes, header)
  2803.  *
  2804.  * Description
  2805.  *
  2806.  *    Creates and returns a compiled method object.  It sets the header of
  2807.  *    the compiled method from "header", and allocates the object so that it
  2808.  *    can hold "numByteCodes" byte codes.
  2809.  *
  2810.  * Inputs
  2811.  *
  2812.  *    numByteCodes: 
  2813.  *        An integer that is the number of byte codes the compiled method
  2814.  *        will contain.
  2815.  *    header: The method header to be used.  Used for placing into the
  2816.  *        compiled method and for figuring out how many literals the
  2817.  *        method has.
  2818.  *
  2819.  * Outputs
  2820.  *
  2821.  *    A compiled method object (NOT an OOP!).
  2822.  */
  2823. static CompiledMethod simpleMethodNew(numByteCodes, header)
  2824. long    numByteCodes;
  2825. MethodHeader header;
  2826. {
  2827.   CompiledMethod method;
  2828.   int        numBytes, numLiterals;
  2829.  
  2830.   numBytes = sizeof(struct CompiledMethodStruct) - sizeof(method->literals);
  2831.   numLiterals = 0;
  2832.  
  2833.   numLiterals = header.numLiterals;
  2834.   numBytes += numLiterals * sizeof(OOP);
  2835.  
  2836.   numBytes += numByteCodes;
  2837.  
  2838.   method = (CompiledMethod)allocObj(ROUNDED_WORDS(numBytes) << 2);
  2839.   method->objSize = ROUNDED_WORDS(numBytes);
  2840.   method->objClass = compiledMethodClass;
  2841.   method->header = header;
  2842.  
  2843.   return (method);
  2844. }
  2845.  
  2846. /*
  2847.  *    Boolean validMethodIndex(methodOOP, index)
  2848.  *
  2849.  * Description
  2850.  *
  2851.  *    Returns true if "index" is in the range of valid indices into the
  2852.  *    instance variables of CompiledMethod "methodOOP".
  2853.  *
  2854.  * Inputs
  2855.  *
  2856.  *    methodOOP: 
  2857.  *        An instance of CompiledMethod.
  2858.  *    index : 1 based integer index into the instance variables of the
  2859.  *        method.
  2860.  *
  2861.  * Outputs
  2862.  *
  2863.  *    True if "index" is within legal range of indices to the instance
  2864.  *    variables of the method.  False otherwise.
  2865.  */
  2866. Boolean validMethodIndex(methodOOP, index)
  2867. OOP    methodOOP;
  2868. long    index;
  2869. {
  2870.   CompiledMethod method;
  2871.  
  2872.   method = (CompiledMethod)oopToObj(methodOOP);
  2873.   /* Written this way to allow a debugging person to see the return value */
  2874.   /* The +2 counts for the description and header */
  2875.   if (index >= 1 && index <= method->header.numLiterals + 2) {
  2876.     return (true);
  2877.   } else {
  2878.     return (false);
  2879.   }
  2880. }
  2881.  
  2882. /*
  2883.  *    OOP compiledMethodAt(methodOOP, index)
  2884.  *
  2885.  * Description
  2886.  *
  2887.  *    Return the object at "index" in CompiledMethod "methodOOP".  Method
  2888.  *    literals currently start at index 3.
  2889.  *
  2890.  * Inputs
  2891.  *
  2892.  *    methodOOP: 
  2893.  *        An instance of CompiledMethod.
  2894.  *    index : A 1 based integer index into the instance variables of the
  2895.  *        method.  The method's descriptor is at index 1, and the method
  2896.  *        header is at index 2.
  2897.  *
  2898.  * Outputs
  2899.  *
  2900.  *    The object at the given index.
  2901.  */
  2902. OOP compiledMethodAt(methodOOP, index)
  2903. OOP    methodOOP;
  2904. long    index;
  2905. {
  2906.   CompiledMethod method;
  2907.   OOP        oop;
  2908.  
  2909.   method = (CompiledMethod)oopToObj(methodOOP);
  2910.   oop = method->literals[index-3]; /* index==1 => descriptor
  2911.                       => literals[index-2-1] */
  2912.   return (oop);
  2913. }
  2914.  
  2915. /*
  2916.  *    void compiledMethodAtPut(methodOOP, index, valueOOP)
  2917.  *
  2918.  * Description
  2919.  *
  2920.  *    Store "valueOOP" into the instance variable of CompiledMethod
  2921.  *    "methodOOP" at "index".
  2922.  *
  2923.  * Inputs
  2924.  *
  2925.  *    methodOOP: 
  2926.  *        A CompiledMethod instance.
  2927.  *    index : A 1 based index into the method's instance variables.  Method
  2928.  *        literals currently start at index 3; the descriptor is stored
  2929.  *        at index 1, and the header is at index 2.
  2930.  *    valueOOP: 
  2931.  *        The object to be stored at the given index.
  2932.  *
  2933.  */
  2934. void compiledMethodAtPut(methodOOP, index, valueOOP)
  2935. OOP    methodOOP, valueOOP;
  2936. long    index;
  2937. {
  2938.   CompiledMethod method;
  2939.  
  2940.   method = (CompiledMethod)oopToObj(methodOOP);
  2941.   prepareToStore(methodOOP, valueOOP);
  2942.   method->literals[index-3] = valueOOP; /*index==1 => descriptor
  2943.                       => literals[index-2-1] */
  2944. }
  2945.  
  2946. /*
  2947.  *    OOP getMethodDescriptor(methodOOP)
  2948.  *
  2949.  * Description
  2950.  *
  2951.  *    Returns the descriptor for the given CompiledMethod.  The descriptor
  2952.  *    contains information about which category the method was stored under,
  2953.  *    and information that can be used to reconstruct the source code for the
  2954.  *    method.
  2955.  *
  2956.  * Inputs
  2957.  *
  2958.  *    methodOOP: 
  2959.  *        OOP of a CompiledMethod instance.
  2960.  *
  2961.  * Outputs
  2962.  *
  2963.  *    Descriptor that was stored in the method, normally a MethodInfo
  2964.  *    instance.
  2965.  */
  2966. OOP getMethodDescriptor(methodOOP)
  2967. OOP    methodOOP;
  2968. {
  2969.   CompiledMethod method;
  2970.  
  2971.   method = (CompiledMethod)oopToObj(methodOOP);
  2972.   return (method->descriptor);
  2973. }
  2974.  
  2975. /*
  2976.  *    void setMethodDescriptor(methodOOP, descriptorOOP)
  2977.  *
  2978.  * Description
  2979.  *
  2980.  *    Sets the method descriptor of "methodOOP" to be "descriptorOOP".
  2981.  *
  2982.  * Inputs
  2983.  *
  2984.  *    methodOOP: 
  2985.  *        OOP of a CompiledMethod.
  2986.  *    descriptorOOP: 
  2987.  *        OOP of a MethodInfo instance which contains the descriptor for
  2988.  *        the CompiledMethod. 
  2989.  *
  2990.  */
  2991. void setMethodDescriptor(methodOOP, descriptorOOP)
  2992. OOP    methodOOP, descriptorOOP;
  2993. {
  2994.   CompiledMethod method;
  2995.  
  2996.   method = (CompiledMethod)oopToObj(methodOOP);
  2997.   if (GCIsOn()) {
  2998.     prepareToStore(methodOOP, descriptorOOP);
  2999.   }
  3000.   method->descriptor = descriptorOOP;
  3001. }
  3002.  
  3003. /*
  3004.  *    static OOP methodInfoNew()
  3005.  *
  3006.  * Description
  3007.  *
  3008.  *    Returns an instance of MethodInfo.  This instance is used in the
  3009.  *    reconstruction of the source code for the method, and holds the
  3010.  *    category that the method belongs to.
  3011.  *
  3012.  * Outputs
  3013.  *
  3014.  *    Instance of MethodInfo used to hold category and source string
  3015.  *    information.
  3016.  */
  3017. static OOP methodInfoNew()
  3018. {
  3019.   MethodInfo    methodInfo;
  3020.  
  3021.   methodInfo = (MethodInfo)newInstance(methodInfoClass);
  3022.   methodInfo->sourceCode = fileSegmentNew();
  3023.   maybeMoveOOP(methodInfo->sourceCode);
  3024.   methodInfo->category = thisCategory;
  3025.  
  3026.   return (allocOOP(methodInfo));
  3027. }
  3028.  
  3029. /*
  3030.  *    static OOP fileSegmentNew()
  3031.  *
  3032.  * Description
  3033.  *
  3034.  *    Returns a FileSegment instance for the currently open compilation
  3035.  *    stream.   FileSegment instances are used record information useful in
  3036.  *    obtaining the source code for a method that's been compiled.  Depending
  3037.  *    on whether the input stream is a string or a FileStream, the instance
  3038.  *    variables are different; for a string, the entire contents of the
  3039.  *    string is preserved as the source code for the method; for a disk file,
  3040.  *    the file name, byte offset and length are preserved.
  3041.  *
  3042.  * Outputs
  3043.  *
  3044.  *    A FileSegment instance that can be used to recover the current method's
  3045.  *    source code.
  3046.  */
  3047. static OOP fileSegmentNew()
  3048. {
  3049.   OOP        fileName, stringContents;
  3050.   FileSegment    fileSegment;
  3051.   int        startPos;
  3052.  
  3053.   switch (getCurStreamType()) {
  3054.   case unknownStreamType:
  3055.     return (nilOOP);
  3056.  
  3057.   case fileStreamType: 
  3058.     fileName = getCurFileName();
  3059.     fileSegment = (FileSegment)newInstance(fileSegmentClass);
  3060.     maybeMoveOOP(fileName);
  3061.     fileSegment->fileName = fileName;
  3062.     startPos = getMethodStartPos();
  3063.     fileSegment->startPos = fromInt(startPos);
  3064.     /* The second -1 removes the terminating '!' */
  3065.     fileSegment->length = fromInt(getCurFilePos() - startPos - 1 - 1);
  3066.     return (allocOOP(fileSegment));
  3067.  
  3068.   case stringStreamType:
  3069.     stringContents = getCurString();
  3070.     return (stringContents);
  3071.  
  3072. #ifdef USE_READLINE
  3073.   case readlineStreamType:
  3074.     stringContents = getCurReadline();
  3075.     return (stringContents);
  3076. #endif /* USE_READLINE */
  3077.   }
  3078. }
  3079.